[Webfunds-commits] java/webfunds/sox/value AccountException.java ValueManager.java AccountStore.java CancelException.java DepositException.java PaymentException.java PendingReceipt.java ReceiptsStore.java SOXServerStore.java StateReceipt.java StoreAccountStore.java StoreReceiptStore.java WalletException.java SOXWallet.java
Ian Grigg
iang@cypherpunks.ai
Sat, 24 Mar 2001 19:57:26 -0400 (AST)
iang 01/03/24 19:57:26
Modified: webfunds/sox/value AccountStore.java CancelException.java
DepositException.java PaymentException.java
PendingReceipt.java ReceiptsStore.java
SOXServerStore.java StateReceipt.java
StoreAccountStore.java StoreReceiptStore.java
WalletException.java
Added: webfunds/sox/value AccountException.java ValueManager.java
Removed: webfunds/sox/value SOXWallet.java
Log:
compiles -- after Repository copy and many changes:
1. repackaging;
2. rewriting of PendingReceipt to use internal class AccountInfo
3. moving SOXWallet copy to ValueManager
4. dropping all WebFunds stuff from ValueManager
Revision Changes Path
1.10 +1 -1 java/webfunds/sox/value/AccountStore.java
Index: AccountStore.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/AccountStore.java,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- AccountStore.java 2001/03/07 00:29:43 1.9
+++ AccountStore.java 2001/03/24 23:57:23 1.10
@@ -1,5 +1,5 @@
-package webfunds.client.sox;
+package webfunds.sox.value;
import webfunds.sox.Account;
import webfunds.sox.AccountId;
1.5 +2 -2 java/webfunds/sox/value/CancelException.java
Index: CancelException.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/CancelException.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- CancelException.java 2000/09/30 18:50:09 1.4
+++ CancelException.java 2001/03/24 23:57:23 1.5
@@ -1,10 +1,10 @@
/*
- * $Id: CancelException.java,v 1.4 2000/09/30 18:50:09 iang Exp $
+ * $Id: CancelException.java,v 1.5 2001/03/24 23:57:23 iang Exp $
*
* Copyright (c) 2000 Systemics Inc on behalf of
* the WebFunds Development Team. All Rights Reserved.
*/
-package webfunds.client.sox;
+package webfunds.sox.value;
/**
* This exception class is thrown when a cancel call fails.
1.6 +2 -2 java/webfunds/sox/value/DepositException.java
Index: DepositException.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/DepositException.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- DepositException.java 2000/09/30 18:50:10 1.5
+++ DepositException.java 2001/03/24 23:57:23 1.6
@@ -1,9 +1,9 @@
-/* $Id: DepositException.java,v 1.5 2000/09/30 18:50:10 iang Exp $
+/* $Id: DepositException.java,v 1.6 2001/03/24 23:57:23 iang Exp $
*
* Copyright (c) 2000 Systemics Inc. on behalf of
* The WebFunds Development Team. All rights reserved.
*/
-package webfunds.client.sox;
+package webfunds.sox.value;
import webfunds.sox.Errors;
1.3 +2 -2 java/webfunds/sox/value/PaymentException.java
Index: PaymentException.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/PaymentException.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- PaymentException.java 2000/09/30 18:50:12 1.2
+++ PaymentException.java 2001/03/24 23:57:23 1.3
@@ -1,10 +1,10 @@
/*
- * $Id: PaymentException.java,v 1.2 2000/09/30 18:50:12 iang Exp $
+ * $Id: PaymentException.java,v 1.3 2001/03/24 23:57:23 iang Exp $
*
* Copyright (c) 2000 Systemics Inc on behalf of
* the WebFunds Development Team. All Rights Reserved.
*/
-package webfunds.client.sox;
+package webfunds.sox.value;
/**
* This exception class is thrown when a request for a payment fails.
1.15 +105 -42 java/webfunds/sox/value/PendingReceipt.java
Index: PendingReceipt.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/PendingReceipt.java,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -r1.14 -r1.15
--- PendingReceipt.java 2001/03/23 15:06:02 1.14
+++ PendingReceipt.java 2001/03/24 23:57:23 1.15
@@ -1,15 +1,16 @@
/*
- * $Id: PendingReceipt.java,v 1.14 2001/03/23 15:06:02 iang Exp $
+ * $Id: PendingReceipt.java,v 1.15 2001/03/24 23:57:23 iang Exp $
*
* Copyright (c) Systemics Ltd 1995-1999 on behalf of
* the WebFunds Development Team. All Rights Reserved.
*/
-package webfunds.client.sox;
+package webfunds.sox.value;
-import webfunds.client.Transaction;
-import webfunds.client.AccountInfo;
import java.io.*;
import java.util.Date;
+
+import webfunds.utils.Panic;
+
import webfunds.sox.Encodable;
import webfunds.sox.ItemId;
import webfunds.sox.Payment;
@@ -18,7 +19,8 @@
/**
* This class is no good.
- * Serializable, so can't change signature. Why has it encode/decode?
+ * This was Serializable, in old SOXWallet, so it may not be
+ * Compatible where serlized classes need to be recovered.
*/
public class PendingReceipt
implements Serializable
@@ -27,14 +29,12 @@
public String transid;
public String getPaymentId() { return transid; }
public ItemId item;
- public AccountInfo source;
- public AccountId getSourceAccountId() { return source.getAccountId(); }
- public AccountInfo target;
- public AccountId getTargetAccountId() { return target.getAccountId(); }
+ public AccountId src;
+ public AccountId tgt;
public long amount;
public byte[] desc;
public Date date;
- public int status = Transaction.STATUS_PENDING;
+ public int status = 0;
public PendingReceipt()
@@ -65,12 +65,12 @@
}
public PendingReceipt(String transid, ItemId item,
- AccountInfo source, AccountInfo target,
+ AccountId src, AccountId tgt,
long amount, byte[] description, Date date)
{
this.transid = transid;
- this.source = source;
- this.target = target;
+ this.src = src;
+ this.tgt = tgt;
this.amount = amount;
this.desc = description;
this.date = date;
@@ -81,13 +81,15 @@
throws IOException
{
DataOutputStream dos = new DataOutputStream(os);
- dos.writeInt(3);
+ dos.writeInt(4);
Encodable.writeString(dos, transid);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
item.encode(baos);
Encodable.writeByteArray(dos, baos.toByteArray() );
- source.encode(os);
- target.encode(os);
+
+ src.encode(os);
+ tgt.encode(os);
+
dos.writeLong(amount);
Encodable.writeByteArray(dos, desc);
dos.writeLong(date.getTime() );
@@ -98,29 +100,90 @@
{
DataInputStream dis = new DataInputStream(is);
int version = dis.readInt();
- if (version == 2)
+ if ( ! (2 <= version && version <= 4) )
+ throw new SOXPacketException("wrong v == " + version);
+
+ transid = Encodable.readString(dis);
+
+ if (version > 2)
+ item = new ItemId(Encodable.readByteArray(dis) );
+
+ if (version < 4)
{
- transid = Encodable.readString(dis);
- source = new AccountInfo(is);
- target = new AccountInfo(is);
- amount = dis.readLong();
- desc = Encodable.readByteArray(dis);
- date = new Date(dis.readLong() );
+ AccountInfo source = new AccountInfo(is);
+ AccountInfo target = new AccountInfo(is);
+ src = source.getAccountId();
+ tgt = target.getAccountId();
}
- if (version == 3)
+ else // the future
{
- transid = Encodable.readString(dis);
- item = new ItemId(Encodable.readByteArray(dis) );
- source = new AccountInfo(is);
- target = new AccountInfo(is);
- amount = dis.readLong();
- desc = Encodable.readByteArray(dis);
- date = new Date(dis.readLong() );
+ src = new AccountId(is);
+ tgt = new AccountId(is);
}
- else
- throw new SOXPacketException("no version number");
+
+ amount = dis.readLong();
+ desc = Encodable.readByteArray(dis);
+ date = new Date(dis.readLong() );
}
+ /*
+ * (taken from webfunds.client.AccountInfo)
+ * Recovery class for remaining AccountInfo layouts that might
+ * be in PendingReceipts.
+ */
+ private class AccountInfo
+ extends Encodable
+ {
+ protected byte[] id;
+
+ public AccountId getAccountId()
+ {
+ AccountId ai = new AccountId();
+ ai.setByteArray(id);
+ return ai ;
+ }
+
+ public AccountInfo(byte[] data)
+ throws IOException
+ {
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ decode(bais);
+ }
+
+ public AccountInfo(InputStream is)
+ throws IOException
+ {
+ decode(is);
+ }
+
+ public void encode(OutputStream os)
+ throws IOException
+ {
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(2);
+ Encodable.writeByteArray(dos, id);
+ Encodable.writeString(dos, "");
+ }
+
+ public void decode(InputStream is)
+ throws IOException
+ {
+ DataInputStream dis = new DataInputStream(is);
+ int version = dis.readInt();
+ if (version != 2)
+ throw new Panic("not a v2 AccountInfo");
+ id = Encodable.readByteArray(dis);
+ String name = Encodable.readString(dis);
+ }
+
+ public String toString()
+ {
+ if (id == null || id.length == 0)
+ return AccountId.open();
+ return getAccountId().toString();
+ }
+
+ }
///////// Self-Test ///////////////////////////////////////
@@ -128,7 +191,7 @@
public String toString()
{
return "PR: " + transid + " " + amount + " of " + item + "\n" +
- "from " + source + " to " + target + "\n";
+ "from " + src + " to " + tgt + "\n";
}
public boolean equals(java.lang.Object obj)
@@ -161,20 +224,20 @@
return false;
}
- AccountInfo ot = other.source;
+ AccountId ot = other.src;
if (ot == null) {
- if (source != null)
+ if (src != null)
return false ;
} else {
- if (!ot.equals(source))
+ if (!ot.equals(src))
return false;
}
- ot = other.target;
+ ot = other.tgt;
if (ot.isOpen()) {
- if (!target.isOpen())
+ if (!tgt.isOpen())
return false ;
} else {
- if (!ot.equals(target))
+ if (!ot.equals(tgt))
return false;
}
@@ -196,8 +259,8 @@
{
Payment p = Payment.example();
Date date = new Date(p.getValidFrom());
- AccountInfo src = new AccountInfo(p.getSource(), null, null);
- AccountInfo tgt = new AccountInfo(p.getTarget(), null, null);
+ AccountId src = p.getSource();
+ AccountId tgt = p.getTarget();
PendingReceipt pr = new PendingReceipt(
p.getId(), p.getItem(),
src, tgt,
1.16 +2 -2 java/webfunds/sox/value/ReceiptsStore.java
Index: ReceiptsStore.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/ReceiptsStore.java,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- ReceiptsStore.java 2000/06/05 04:23:40 1.15
+++ ReceiptsStore.java 2001/03/24 23:57:23 1.16
@@ -1,9 +1,9 @@
-/* $Id: ReceiptsStore.java,v 1.15 2000/06/05 04:23:40 gelderen Exp $
+/* $Id: ReceiptsStore.java,v 1.16 2001/03/24 23:57:23 iang Exp $
*
* Copyright (c) Systemics Inc. 1999-2000 on behalf of
* The WebFunds Development Team. All Rights Reserved.
*/
-package webfunds.client.sox;
+package webfunds.sox.value;
import webfunds.sox.AccountId;
1.12 +2 -2 java/webfunds/sox/value/SOXServerStore.java
Index: SOXServerStore.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/SOXServerStore.java,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- SOXServerStore.java 2000/07/14 00:17:59 1.11
+++ SOXServerStore.java 2001/03/24 23:57:23 1.12
@@ -1,10 +1,10 @@
/*
- * $Id: SOXServerStore.java,v 1.11 2000/07/14 00:17:59 iang Exp $
+ * $Id: SOXServerStore.java,v 1.12 2001/03/24 23:57:23 iang Exp $
*
* Copyright (c) 1995-2000 Systemics Inc. on behalf of
* the WebFunds Development Team. All Rights Reserved.
*/
-package webfunds.client.sox;
+package webfunds.sox.value;
import java.util.Enumeration;
1.4 +2 -2 java/webfunds/sox/value/StateReceipt.java
Index: StateReceipt.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/StateReceipt.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- StateReceipt.java 2000/07/16 19:27:23 1.3
+++ StateReceipt.java 2001/03/24 23:57:24 1.4
@@ -1,10 +1,10 @@
/*
- * $Id: StateReceipt.java,v 1.3 2000/07/16 19:27:23 iang Exp $
+ * $Id: StateReceipt.java,v 1.4 2001/03/24 23:57:24 iang Exp $
*
* Copyright (c) 2000 Systemics Inc on behalf of
* the WebFunds Development Team. All Rights Reserved.
*/
-package webfunds.client.sox;
+package webfunds.sox.value;
import java.io.*;
import java.security.*;
1.24 +6 -6 java/webfunds/sox/value/StoreAccountStore.java
Index: StoreAccountStore.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/StoreAccountStore.java,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -r1.23 -r1.24
--- StoreAccountStore.java 2001/03/07 00:29:43 1.23
+++ StoreAccountStore.java 2001/03/24 23:57:24 1.24
@@ -1,10 +1,10 @@
/*
- * $Id: StoreAccountStore.java,v 1.23 2001/03/07 00:29:43 iang Exp $
+ * $Id: StoreAccountStore.java,v 1.24 2001/03/24 23:57:24 iang Exp $
*
* Copyright (c) Systemics Ltd 1995-1999 on behalf of
* the WebFunds Development Team. All Rights Reserved.
*/
-package webfunds.client.sox;
+package webfunds.sox.value;
import java.util.Enumeration;
import java.io.*;
@@ -22,7 +22,7 @@
{
Store store;
- IssuerFinder finder;
+// IssuerFinder finder;
/**
* Store for accounts.
@@ -33,14 +33,13 @@
if (store == null)
throw new IllegalArgumentException("StoreAccountStore store==null");
this.store = store;
- this.finder = null;
+// this.finder = null;
//logmsg("SAS " + this.getClass().getClassLoader());
}
/**
* Store for accounts.
* @option finder is set in all accounts that are requested.
- */
public StoreAccountStore(Store store, IssuerFinder finder)
{
if (store == null)
@@ -48,6 +47,7 @@
this.store = store;
this.finder = finder;
}
+ */
public Account[] getAllAccounts()
@@ -153,7 +153,7 @@
throw new StoreException("Unknown object from " + key + ": " +
obj.getClass().getName());
- ac.setFinder(finder);
+ // ac.setFinder(finder);
return ac;
}
1.33 +6 -6 java/webfunds/sox/value/StoreReceiptStore.java
Index: StoreReceiptStore.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/StoreReceiptStore.java,v
retrieving revision 1.32
retrieving revision 1.33
diff -u -r1.32 -r1.33
--- StoreReceiptStore.java 2001/03/07 00:29:43 1.32
+++ StoreReceiptStore.java 2001/03/24 23:57:24 1.33
@@ -1,10 +1,10 @@
/*
- * $Id: StoreReceiptStore.java,v 1.32 2001/03/07 00:29:43 iang Exp $
+ * $Id: StoreReceiptStore.java,v 1.33 2001/03/24 23:57:24 iang Exp $
*
* Copyright (c) Systemics Ltd 1999 on behalf of
* the WebFunds Development Team. All Rights Reserved.
*/
-package webfunds.client.sox;
+package webfunds.sox.value;
import java.util.Enumeration;
import java.util.Hashtable;
@@ -485,8 +485,8 @@
for (int i = 0; i < pendings.length; i++)
{
pending = pendings[i];
- AccountId src = pending.getSourceAccountId();
- AccountId tgt = pending.getTargetAccountId();
+ AccountId src = pending.src;
+ AccountId tgt = pending.tgt;
boolean fromMe = acct.equals(src);
boolean toMe = acct.equals(tgt);
@@ -693,8 +693,8 @@
AccountId tgt = new AccountId();
AccountId src = new AccountId();
- tgt.setByteArray(pend.target.getByteArray());
- src.setByteArray(pend.source.getByteArray());
+ tgt.setByteArray(pend.tgt.getByteArray());
+ src.setByteArray(pend.src.getByteArray());
bug.println(" P" + src + " ==> " + tgt);
//
1.8 +2 -2 java/webfunds/sox/value/WalletException.java
Index: WalletException.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/WalletException.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- WalletException.java 2000/11/09 13:28:20 1.7
+++ WalletException.java 2001/03/24 23:57:24 1.8
@@ -1,10 +1,10 @@
/*
- * $Id: WalletException.java,v 1.7 2000/11/09 13:28:20 iang Exp $
+ * $Id: WalletException.java,v 1.8 2001/03/24 23:57:24 iang Exp $
*
* Copyright (c) 2000 Systemics Inc on behalf of
* the WebFunds Development Team. All Rights Reserved.
*/
-package webfunds.client.sox;
+package webfunds.sox.value;
import webfunds.sox.Errors;
import webfunds.sox.SOXException;
1.1 java/webfunds/sox/value/AccountException.java
Index: AccountException.java
===================================================================
/*
* $Id: AccountException.java,v 1.1 2001/03/24 23:57:23 iang Exp $
*
* Copyright (c) 2000 Systemics Inc on behalf of
* the WebFunds Development Team. All Rights Reserved.
*/
package webfunds.sox.value;
/**
* This exception class is thrown when a cancel call fails.
* For high-level but non-GUI wallet calls.
*/
public class AccountException
extends WalletException
{
public AccountException(String msg) { super(msg); }
public AccountException(int errno) { super(errno); }
public AccountException(int errno, String msg) { super(errno, msg); }
public String toString() { return "CanEx: " + super.toString(); }
}
1.1 java/webfunds/sox/value/ValueManager.java
Index: ValueManager.java
===================================================================
/* $Id: ValueManager.java,v 1.1 2001/03/24 23:57:24 iang Exp $
*
* Copyright (c) Systemics Inc. 1995-2000 on behalf of
* The WebFunds Development Team. All Rights Reserved.
*/
package webfunds.sox.value;
// JDK
import java.io.*;
import java.net.*;
import java.util.*;
import java.security.SecureRandom;
// SOX toolkit
import webfunds.ricardian.Contract;
import webfunds.ricardian.ContractStore;
import webfunds.ricardian.SOXServerException;
import webfunds.utils.Debug;
import webfunds.utils.Hex;
import webfunds.utils.Panic;
import webfunds.store.Store;
import webfunds.store.StoreException;
import webfunds.store.SepFileStore;
import webfunds.sox.Account;
import webfunds.sox.AccountId;
import webfunds.sox.ArmouredPayment;
import webfunds.sox.Crypto;
import webfunds.sox.IssuerFinder;
import webfunds.sox.ItemId;
import webfunds.sox.MailId;
import webfunds.sox.MailItem;
import webfunds.sox.AbstractPayment;
import webfunds.sox.PaymentFactory;
import webfunds.sox.Payment;
import webfunds.sox.TokenPayment;
import webfunds.sox.Token;
// import webfunds.sox.RandomToken;
import webfunds.sox.Receipt;
import webfunds.sox.SOXAccountException;
import webfunds.sox.SOXArgsException;
import webfunds.sox.SOXDepositException;
import webfunds.sox.SOXException;
import webfunds.sox.SOXLaterException;
import webfunds.sox.SOXPacketException;
import webfunds.sox.SOXSubAccountException;
import webfunds.sox.SubAccount;
import webfunds.sox.ValueAccount;
/**
* The SOX Value Manager.
*
* (Was the SOXWallet -- attempt to separate and make simple!)
*
* @version $Revision: 1.1 $
*/
public class ValueManager
extends Debug
{
/**
* Store is the interface to storage in the outside world.
*/
protected Store store;
protected AccountStore accountStore;
protected SOXServerStore soxes;
protected ReceiptsStore receiptStore;
protected Properties properties;
protected IssuerFinder finder;
protected static final String name = "SOX Value Manager";
protected String shortname = "SOX-VM";
protected static final String fix = " Sv: ";
/** If we can do things that would normall be illegal or immoral */
public boolean isTestMode()
{
if (properties == null)
return false;
String tmode = (String)properties.get("test.mode");
return (tmode != null) && tmode.equals("true");
}
///////// Initialisation //////////////////////////////
// bug gets set directly by caller?
public ValueManager()
{
super();
debug(fix);
}
/**
* Initialise our external interfaces.
*
* Construction is complicated by class loader, hence separate init.
*/
public void init(SecureRandom sr, Store store,
ContractStore contracts, Properties properties)
throws StoreException
{
if (Crypto.sr == null)
Crypto.setSecureRandom(sr);
this.properties = properties;
setStore(store, contracts);
}
/**
* Shortname is used for the stores. Can be set by child.
public void setShortName(String name) { shortname = name; }
public String getShortName() { return shortname; }
public String getProtocol() { return name; }
public String getVersion() { return "2.2"; }
*/
/**
* Set the Store.
* This is the only access we have to persistant storage.
* Unfortunately, it's quite limiting.
*/
protected void setStore(Store store, ContractStore contracts)
throws StoreException
{
this.store = store;
//
// The SOX server file Store.
//
Store soxstore = null;
try {
soxstore = store.getStore("SOXServers", SepFileStore.APPEND);
} catch (StoreException ex) { // should delete and start again
// ex.printStackTrace(err());
logmsg("Failed to get SOXServers store: " + ex);
// System.exit(1);
throw ex ;
}
soxes = new SOXServerStore(soxstore, getDebug());
soxes.setContractStore(contracts);
finder = (IssuerFinder)soxes;
//
// Account store.
// Failures here are unrecoverable.
//
logmsg("Trying to get 'Accounts' store");
Store st = store.getStore("Accounts");
logmsg(
" We got: " + st +
", with size: " + st.size() );
StoreAccountStore accs;
accs = new StoreAccountStore(st);
accs.debug(bug);
accountStore = (AccountStore)accs;
//
// Receipt store.
// Failures here are unrecoverable.
//
receiptStore = new StoreReceiptStore(store.getStore("Receipts"));
//receiptStore.debug(bug);
//
// An old store is Issuers. We should remove that some time.
//
}
///////// Acquire Things //////////////////////////////
/**
* Lowest level get Account call - here we fix up the account for
* actual usage (in the store it is only kept as data).
*/
protected Account getAccount(AccountId id)
throws StoreException
{
Account ac = accountStore.getAccount(id);
ac.setFinder(finder);
return ac;
}
/**
* Call this one if you know the account is there,
* and you don't want to handle the exception.
*/
protected Account getAccountOrDie(AccountId id)
{
Account acct = null;
try {
acct = getAccount(id);
} catch (StoreException ex) {
ex.printStackTrace(System.err);
logmsg("account " + id);
logmsg("no such account? " + ex);
System.exit(1);
}
return acct ;
}
/**
* @return true if the account exists, else false
*/
public boolean existsAccount(AccountId id)
{
Account acct = null;
try {
acct = getAccount(id);
} catch (StoreException ex) {
return false;
}
return true;
}
/**
* @return true if the account and subaccount exists, else false
*/
public boolean existsSubAccount(AccountId id, ItemId item)
{
Account acct = null;
try {
acct = getAccount(id);
} catch (StoreException ex) {
return false;
}
if (acct.subExists(item))
return true;
return false;
}
/**
* Get a list of accounts managed by this wallet.
*
* @return an array of all AccountIds, maybe empty but never null
*/
public AccountId[] getAccountIds()
{
Account[] accounts;
try {
accounts = accountStore.getAllAccounts();
} catch (StoreException ex) {
ex.printStackTrace();
return new AccountId[0];
}
if (accounts == null)
return new AccountId[0];
int len = accounts.length;
logmsg("Retrieved " + len + " accounts");
AccountId[] ids = new AccountId[len];
for (int i = 0; i < len; i++)
{
ids[i] = accounts[i].getId();
}
return ids;
}
/**
* Get the item identifiers for the stated account.
*
*/
public ItemId[] getItemIds(AccountId accountid)
throws StoreException
{
Account acct = getAccount(accountid);
ItemId[] items = acct.getItemIds();
logmsg("Ac " + acct + " : " + items.length + " items!");
for (int i = 0; i < items.length; i++)
logmsg(" " + items[i]);
return items;
}
/**
* Get a known account.
* @return the account, never null
* @except AccountException if the account is unknown or not available
*/
protected Account getKnownAccount(AccountId accountid)
throws AccountException
{
Account account;
try
{
account = getAccount(accountid);
} catch (StoreException ex) {
ex.printStackTrace();
throw new AccountException(ex.toString());
}
if (account == null)
throw new AccountException("ac not known");
return account;
}
/**
* Get a known sub account (via the parent account).
* @return the subaccount, never null
* @except AccountException if the account or sub is unknown / not avail
*/
protected SubAccount getKnownSubAccount(AccountId accountid, ItemId item)
throws AccountException
{
logmsg("AccountId update");
Account account = getKnownAccount(accountid);
SubAccount sub = account.getSub(item);
if (sub == null)
throw new AccountException("no such sub: " + item);
return sub;
}
/**
* Get a known sub account (via the parent account).
* @return the subaccount, never null
* @except AccountException if the account or sub is unknown / not avail
*/
protected ValueAccount getKnownValueAccount(AccountId accountid, ItemId item)
throws AccountException
{
SubAccount sub = getKnownSubAccount(accountid, item);
if (!(sub instanceof ValueAccount))
throw new AccountException("sub not a ValueAccount: " + item);
return (ValueAccount)sub;
}
public long getValue(AccountId acct, ItemId item, boolean pending)
{
// I moved the contents to StoreReceiptStore, which better knows
// about changes. There were some logical ???s in the old one.
try
{
if (pending)
return receiptStore.pendingValue(acct, item) ;
else
return receiptStore.confirmedValue(acct, item) ;
}
catch (StoreException ex)
{
logmsg("getValue: " + ex);
}
return Long.MIN_VALUE ;
}
/////////// Payments //////////////////////////////////////////
private boolean gotEnough(AccountId src,
ItemId item,
long amount)
{
long pend = getValue(src, item, true);
long confirmed = getValue(src, item, false);
logmsg("amount: " + amount + ", pend: " + pend +
", confirmed: " + confirmed);
if ( (pend + confirmed) < amount )
return false;
else
return true;
}
/**
* Make a payment.
* This should be used by all automatic clients, it does:
* * access to subaccounts,
* * checks balances, and
* * saves the pending payment for later reconciliation.
* and delivers the payment back. It is thread safe.
*
* @param tgt the target account, may be open
* @param src the source account where funds are written from
* @param item is the name of the item
* @param amount the (contract) qty of items of which the payment is for
* @param desc a description of what this payment is for (optional)
* @param boolean whether or not the payment is a rollover payment
* @param from the time from which the payment is valid
* @param till the time at which the payment will expire
* @param pid a payment identifier, ignored if empty
*/
public synchronized AbstractPayment makePayment(
AccountId src, AccountId tgt,
ItemId item, long amount,
byte[] desc,
long from, long till,
String pid)
throws PaymentException, AccountException
{
if (amount < 0)
throw new IllegalArgumentException("amount " + amount + " < 0 !");
ValueAccount val = getKnownValueAccount(src, item);
// it is an error (?) if the user asks for pid when
// that one is already in play.
if (pid != null && (pid.length() > 0))
{
StateReceipt sr;
try {
sr = receiptStore.getReceipt(src, item, pid);
} catch (StoreException ex) {
throw new webfunds.utils.Panic("get StateReceipt for " + pid);
}
if (sr != null)
{
throw new PaymentException(PaymentException.PID_IN_USE, pid);
}
}
/*
* Check if there is enough.
* (There is always "enough" if the src is equal to the target
* or if the amount is zero... This is a judgement call of course.)
*/
if (!src.equals(tgt) && (amount > 0) &&
!gotEnough(src, item, amount))
{
throw new PaymentException(PaymentException.NOT_ENUF_FUNDS,
"amount " + amount + " not available");
}
Payment payment;
try {
payment = val.createPayment(tgt, amount,
desc, false,
from, till,
pid);
} catch (SOXException ex) {
ex.printStackTrace();
throw new PaymentException(PaymentException.UNKNOWN,
"Payment no good: " + ex);
}
savePaymentAsPending(src, payment);
return payment ;
}
/**
* Save a payment made as Pending.
*
* @param pay the Payment written out but not as yet saved
* @except PaymentException if the save could not be effected
*/
protected void savePaymentAsPending(AccountId src, AbstractPayment pay)
throws PaymentException
{
//
// Have to create and store the PendingReceipt securely
// before returning the payment to the caller. Once
// returned, we have lost control of it, so must have the
// PendingReceipt receipt there for matching or cancelling.
//
PendingReceipt pending = new PendingReceipt(pay.getId(),
pay.getItem(),
pay.getSource(), pay.getTarget(),
pay.getQty(),
pay.getDesc(),
new Date());
try {
receiptStore.addPendingReceipt(pending, src);
} catch (StoreException ex) {
ex.printStackTrace();
throw new PaymentException(PaymentException.UNKNOWN,
"Error saving Pending: " + ex);
}
}
/**
* Make a Rollover payment.
* This should be used by low level clients. It checks:
* * access to subaccounts,
* * saves the pending payment for later reconciliation.
* and delivers the payment back.
*
* It does not check balances (by definition).
* It is thread safe. Why?
*
* @param tgt the target account, should not be open, where funds go
* @param src the source account (to be frozen) where funds are
* @param item is the name of the contract, but this is only used
* to identify the issuer, all contracts at that issuer effected
* @param desc a description of what this payment is for (optional)
* @param boolean whether or not the payment is a rollover payment
* @param from the time from which the payment is valid
* @param till the time at which the payment will expire
*/
public synchronized Payment makeRollover(AccountId src, AccountId tgt,
ItemId item,
byte[] desc,
long till)
throws PaymentException, AccountException
{
ValueAccount val = getKnownValueAccount(src, item);
/*
* There is always "enough" for a rollover...
*/
Payment payment;
try {
payment = val.createPayment(tgt,
0, /* value doesn't matter */
desc,
true, /* the flag that makes it Rollover */
System.currentTimeMillis(),
till,
null
);
} catch (SOXException ex) {
ex.printStackTrace();
throw new PaymentException(PaymentException.UNKNOWN,
"Payment no good: " + ex);
}
savePaymentAsPending(src, payment);
return payment ;
}
/**
* Make a Token payment.
* This should be used by all automatic clients, it does:
* * access to subaccounts,
* * checks balances, and
* * saves the pending payment for later reconciliation.
* and delivers the payment back. It is thread safe.
*
* @param tgt the target account, may be open
* @param src the source account where funds are written from
* @param item is the name of the contract
* @param amount the (contract) qty of items of which the payment is for
* @param desc a description of what this payment is for (optional)
* @param boolean whether or not the payment is a rollover payment
* @param from the time from which the payment is valid
* @param till the time at which the payment will expire
* @param type the type of token method from PaymentFactory
*/
public synchronized TokenPayment makeTokenPayment(
AccountId src, AccountId tgt,
ItemId item, long amount,
byte[] desc,
long from, long till,
int type)
throws PaymentException, AccountException
{
ValueAccount val = getKnownValueAccount(src, item);
if (amount < 0)
throw new IllegalArgumentException("amount " + amount + " < 0 !");
/*
* Check if there is enough.
* (There is always "enough" if the src is equal to the target
* or if the amount is zero... This is a judgement call of course.)
*/
if ((amount > 0) &&
!gotEnough(src, item, amount))
{
throw new PaymentException(PaymentException.NOT_ENUF_FUNDS,
"amount " + amount + " not available");
}
if (desc == null)
desc = new byte[0];
/*
* We need two payments: one is the proto token payment,
* the other is the cash to pay for it. Both need to be
* written, then saved. Then, we can attempt a deposit
* of the primary payment in order to pay for the proto.
*
* We need to write a payment to someone who can cash these
* payments ... it has to be open, as we don't otherwise
* know the name of the float as yet.
*/
Payment pay;
try {
pay = val.createPayment(new AccountId(), amount,
desc, false,
from, till,
null);
} catch (SOXException ex) {
ex.printStackTrace();
throw new PaymentException(ex.getNumber(), "SOXEx: " + ex);
}
Token[] tokens;
try {
tokens = PaymentFactory.getProtoTokens(type, amount);
} catch (SOXArgsException ex) {
ex.printStackTrace();
throw new PaymentException(PaymentException.UNKNOWN_TYPE,
"Token Type " + type + " not supported");
}
// if (type == PaymentFactory.RANDOM_TOKEN)
// tokens = RandomToken.getProtoTokens(amount);
// else
// {
// throw new PaymentException(PaymentException.UNKNOWN_TYPE,
// "Token Type " + type + " not supported");
// }
TokenPayment proto;
String pid = pay.getId();
try {
proto = val.createTokenPayment(tokens, desc, pid + "+");
} catch (SOXException ex) {
ex.printStackTrace();
throw new PaymentException(ex.getNumber(), "SOXEx: " + ex);
}
savePaymentAsPending(src, pay);
// actually, what is the point of saving a proto payment?
// savePaymentAsPending(src, proto);
//
// Beyond this point, failure leaves a dangling payment,
// cancel manually for now.
//
MailItem[] mails;
try {
mails = val.withdraw(pay, proto, new String(desc), null);
} catch (SOXLaterException ex) {
throw new PaymentException(ex.getNumber(), "Later: " + ex);
} catch (SOXSubAccountException ex) {
throw new PaymentException(ex.getNumber(), "SOXSAEx: " + ex);
} catch (SOXDepositException ex) {
throw new PaymentException(ex.getNumber(), "SOXDepEx: " + ex);
} catch (SOXException ex) {
throw new PaymentException(ex.getNumber(), "SOXEx: " + ex);
}
logmsg("received mails: " + mails.length);
//
// By convention, if mails are returned, 1st one is
// the one for this deposit.
// Others are for the (sub?) account.
// (Actually, as implemented, there is only one mail.)
//
if (mails == null)
{
throw new PaymentException(PaymentException.UNKNOWN,
"Withdraw partial (no mail returned) try an update ?");
}
/*
* As we have no clue who the payment went to, extract it
* out of the receipt so we can ... check it against the
* receipt.
*/
byte[] b = mails[0].getMessage();
Receipt receipt;
try {
receipt = new Receipt(b);
} catch (SOXPacketException ex) {
throw new PaymentException(ex.getNumber(),
"SOXPEx on receipt: " + ex);
}
AccountId ptt = receipt.getTarget();
String s = checkReceipt(mails[0], src, ptt, item, pay.getQty());
if (s != null)
{
throw new PaymentException(PaymentException.UNKNOWN,
"cancel receipt is not good: " + s);
}
internalUpdate(val, mails);
AbstractPayment pp = receipt.getExchangePayment();
if (pp == null && src.equals(receipt.getTarget()))
{
throw new PaymentException(PaymentException.UNKNOWN_TYPE,
"no Payment returned in Withdrawal"+
"\n\n(Issuer does not support this type of Payment)");
}
if ( !(pp instanceof TokenPayment) )
{
throw new PaymentException(PaymentException.UNKNOWN,
"AB is not TP? : " + pp.getClass() +
"\n\n" + pp.toString());
}
TokenPayment tp = (TokenPayment)pp;
if (!tp.isSigned())
{
throw new PaymentException(PaymentException.UNKNOWN,
"TP not signed? : " + tp.getClass() +
"\n\n" + pp.toString());
}
return tp;
}
/**
public final static String SOX_MESSAGE = "SOX MESSAGE";
* Make an ASCII-armoured message from a payment,
* a la PGP armouring. Filters out exceptions.
*
* Low Level Wallet Interface.
public static byte[] asciiArmour(AbstractPayment pay)
throws PaymentException
{
if (pay == null)
throw new PaymentException("null");
ArmouredPayment ap = new ArmouredPayment(pay);
return ap.encode();
}
*/
/**
* Decoder of SOX armoured messages.
* @return a good payment
* @throw PaymentException if payment could not be decoded
* NOT USED?
public static AbstractPayment decodeSOXPayment(byte[] data)
throws PaymentException
{
if (data == null)
throw new PaymentException("decodeSOXPayment(null) ?");
ArmouredPayment ap;
try {
ap = new ArmouredPayment(data);
} catch (SOXPacketException ex) {
throw new PaymentException("SOXPEx: " + ex);
}
AbstractPayment pay = ap.getPay();
return pay;
}
*/
/////////// Deposit //////////////////////////////////////////
private String _func;
private void ENTER(String s) { this._func = s; }
private void TRACE(String s)
{
System.err.println( "Wallet." + this._func + ": " + s );
}
/**
* Deposit the given payment.
*
* @param pay for deposit
* @param account is the account to use
* @param desc description
* @param idempotentId is suggested id for deposit, ignored if null/empty
*
* @throw AccountException if the target account does not exist, or
* does not include the item as a subaccount
*/
public Receipt doDeposit(AbstractPayment pay, AccountId accountid,
byte[] desc, String idempotentId)
throws DepositException, AccountException
{
ValueAccount val = getKnownValueAccount(accountid, pay.getItem());
String choice = val.getAccount().getName();
logmsg("ready to deposit payment using " + choice + "\n" + pay);
if (desc == null)
desc = new byte[0];
ItemId item = pay.getItem();
MailItem[] mails;
try {
mails = val.deposit(pay, new String(desc), idempotentId);
} catch (SOXDepositException ex) { // webfunds.sox.Errors
throw new DepositException(ex.getNumber(), ex.getMessage());
} catch (SOXLaterException ex) {
throw new DepositException(ex.getNumber(), ex.getMessage());
} catch (SOXSubAccountException ex) { // negative
ex.printStackTrace();
throw new DepositException(ex.getNumber(), ex.getMessage());
} catch (SOXArgsException ex) {
ex.printStackTrace();
throw new Panic("args failed: " + ex);
} catch (SOXException ex) {
ex.printStackTrace();
throw new Panic("unknown exception: " + ex);
}
logmsg("received mails: " + mails.length);
//
// By convention, if mails are returned, 1st one is
// the one for this deposit.
// Others are for the (sub?) account.
// (Actually, as implemented, there is only one mail.)
//
if (mails == null)
throw new Panic(
"Deposit partial (no mail returned) try an update...");
AccountId ptt = pay.getTarget();
AccountId pss = pay.getSource();
long qty = pay.getQty();
String s = checkReceipt(mails[0], pss, ptt, item, qty);
if (s != null)
/// hmmm this is really a SubAccount failure, should never happen
throw new DepositException(SOXSubAccountException.NO_RECEIPT);
internalUpdate(val, mails);
Receipt rcpt = mailItemToReceipt( mails[0] );
if( rcpt == null )
/// hmmm this is really a SubAccount failure, should never happen
throw new DepositException(SOXSubAccountException.NO_RECEIPT);
return rcpt;
}
/**
* Check the receipt conforms to what was expected.
* @return an error string if not conformant, else null
*/
public String checkReceipt(MailItem mail,
AccountId p_src, AccountId p_tgt,
ItemId p_item, long amount)
{
byte[] b = mail.getMessage();
Receipt receipt;
try {
receipt = new Receipt(b);
} catch (SOXPacketException ex) {
return "could not recover receipt: " + ex ;
}
return checkReceipt(receipt, p_src, p_tgt, p_item, amount);
}
/**
* Convert a MailItem to Receipt.
*
* @return Receipt or null
*/
private Receipt mailItemToReceipt(MailItem mailItem)
{
try
{
return new Receipt( mailItem.getMessage() );
}
catch(SOXPacketException e1)
{
return null;
}
}
/**
* Check the receipt conforms to what was expected.
* @return an error string if not conformant, else null
*/
public String checkReceipt(Receipt receipt,
AccountId p_src, AccountId p_tgt,
ItemId p_item, long p_qty)
{
String statString = "";
String s = "";
AccountId tgt = receipt.getTarget();
AccountId src = receipt.getSource();
ItemId item = receipt.getItem();
long qty = receipt.getQty();
logmsg("checking: " +
tgt + "==" + p_tgt + " " +
src + "==" + p_src + " " +
item + "==" + p_item + " " +
qty + "==" + p_qty);
if (!tgt.equals(p_tgt))
statString += "\nTarget " + tgt + " is not expected " + p_tgt;
if (!src.equals(p_src))
statString += "\nSource " + tgt + " is not expected " + p_src;
if (!item.equals(p_item))
statString += "\nItem " + item + " is not expected " + p_item;
if (qty != p_qty)
statString += "\nAmount " + qty + " is not expected " + qty;
return (s.length() == 0) ? null : s ;
}
/////////// Cancel ////////////////////////
/**
* Cancel a Payment (pid) within a SubAccount.
* And call the update. For high-level user programs.
*
*/
public void cancel(AccountId accountid, ItemId item, String pid)
throws CancelException, SOXLaterException, AccountException
{
ValueAccount val = getKnownValueAccount(accountid, item);
internalCancel(val, pid);
}
/**
* Return the full state (machine) for a transaction.
*
* Low Level Wallet Interface
*
* @return a StateReceipt, or null if the pid is not found
*/
public StateReceipt getTransactionState(AccountId acct,
ItemId item, String id)
{
StateReceipt sr;
try {
sr = receiptStore.getReceipt(acct, item, id);
} catch (StoreException ex) {
throw new webfunds.utils.Panic("get StateReceipt for " + id);
}
return sr ;
}
/**
* Cancel Payments (by pids) within a SubAccount
* before they hit the server.
*
*/
protected void internalCancel(ValueAccount sub, String[] pids)
throws CancelException, SOXLaterException
{
AccountId acct = getAccountId(sub);
ItemId item = sub.getItemId();
logmsg("internalCancel("+acct+"/"+item+", pid["+pids.length+"])");
Vector updates = new Vector();
SOXLaterException soxlater = null;
StoreException storex = null;
int failures = 0;
String errors = "";
for (int i = 0; i < pids.length; i++)
{
String pid = pids[i];
StateReceipt sr;
try {
sr = receiptStore.getReceipt(acct, item, pid);
} catch (StoreException ex) {
storex = ex ;
errors += "\nStoreEx: " + ex;
break ;
}
if ((sr != null) && sr.isComplete())
continue ;
MailItem[] mails;
try {
mails = quietCancel(sub, pid);
} catch (SOXLaterException ex) {
soxlater = ex ;
errors += "\nLater: " + ex;
break ;
} catch (CancelException ex) {
failures++;
errors += "\npid "+pid+" failed to cancel (skipping): "+ex;
continue ;
}
byte[] b = mails[0].getMessage();
Receipt receipt;
try {
receipt = new Receipt(b);
} catch (SOXPacketException ex) {
failures++;
errors += "\npid "+pid+"could not recover receipt: " + ex ;
continue ;
}
String s = checkReceipt(receipt, acct, acct, item, 0);
if (s != null)
{
// if we get this far, and we get a receipt, it must
// be a good one. what went wrong?
throw new CancelException(CancelException.SERVER,
"receipt is not as expected: " + s);
}
// hey, is this necessary? It gets updated later anyway.
sr.setCancelled(receipt);
// put new sr!
for (int j = 0; j < mails.length; j++)
updates.addElement(mails[j]);
}
int num = updates.size();
if (num > 0)
{
MailItem[] confirms = new MailItem[num];
updates.copyInto(confirms);
//
// Should this be here? Perhaps the low level wallet
// should not try and force the update? Still have
// to do something with the mails.
//
internalUpdate(sub, confirms);
errors = "updated " + num + " mails.\n" + errors;
}
if (storex != null)
throw new CancelException(CancelException.INTERNAL, errors);
if (failures > 0)
throw new CancelException(errors) ;
if (soxlater != null)
throw soxlater ;
return ;
}
/**
* Cancel a Payment (pid) within a SubAccount.
*
* @return an error string if errors detected, else empty (never null)
*/
protected void internalCancel(ValueAccount sub, String pid)
throws CancelException, SOXLaterException
{
AccountId acct = getAccountId(sub);
ItemId item = sub.getItemId();
logmsg("internalCancel(" + acct + "/" + item + ", " + pid + ")");
StateReceipt sr;
try {
sr = receiptStore.getReceipt(acct, item, pid);
} catch (StoreException ex) {
throw new CancelException(CancelException.INTERNAL,ex.getMessage());
}
if ((sr != null) && sr.isSucceeded())
throw new CancelException(CancelException.TOO_LATE);
if ((sr != null) && sr.isCancelled())
throw new CancelException(CancelException.ALREADY);
MailItem[] mails = quietCancel(sub, pid);
//
// Should this be here? Perhaps the low level wallet
// should not try and force the update? Still have
// to do something with the mails.
//
internalUpdate(sub, mails);
return ;
}
/**
* Cancel many Payments (pid) within a SubAccount.
* Not yet used, more here as a placeholder.
*
* Low Level Wallet Interface
*
* @return an error string if errors detected, else empty (never null)
*/
protected void quietCancel(ValueAccount sub, String[] pids)
{
throw new Panic("not implemented");
}
/**
* Cancel a Payment (pid) within a SubAccount.
* This can be used by other wallets, but be
* careful to complete the update and store by calling
* internalUpdate(SubAccount, MailItem[])
*
* Low Level Wallet Interface
*
* @return Mails received, first of which is the applicable one
*/
protected MailItem[] quietCancel(ValueAccount sub, String pid)
throws SOXLaterException, CancelException
{
//
// In theory, this doesn't change "state" as the important
// event, the writing and saving of the payment, is done
// elsewhere.
//
logmsg("cancelling " + pid);
MailItem[] mails;
try {
mails = sub.cancel(pid);
// } catch (SOXLaterException ex) {
// return try_again_later;
// } catch (SOXSubAccountException e) {
} catch (SOXException ex) {
ex.printStackTrace(err());
throw new CancelException(CancelException.INTERNAL,
"SubAccount broken?) :\n " + ex);
}
//
// By convention, if mails are returned, 1st one is
// the one for this deposit (cancel).
// (Actually, as implemented, will only be one.)
// But, if there is an error from subaccount, how to pass back?
// an empty array should be an exception!
//
if (mails == null)
throw new CancelException(CancelException.SERVER,
"Cancel failed? (no mail returned)");
AccountId acct = getAccountId(sub);
String s = checkReceipt(mails[0], acct, acct, sub.getItemId(), 0);
if (s != null)
{
// if we get this far, and we get a receipt, it must
// be a good one. what went wrong?
throw new CancelException(CancelException.SERVER,
"receipt is not as expected: " + s);
}
return mails ;
}
/////////// Account / SubAccount manipulations ////////////////////////
/**
* Create a new account in the Wallet and return it's AccountId.
*
* @param a string name, may be null
* @return a valid AccountId, never null
* @excep AccountException for any errors
*/
public AccountId createAccount(String name)
throws AccountException
{
Account acc;
try {
acc = Account.getInstance();
} catch(SOXAccountException e) {
throw new AccountException(e.toString());
}
acc.setName(name);
logmsg("createAccount: Created new account");
AccountId accId = acc.getId();
try {
this.accountStore.addAccount(acc);
this.getAccount(accId);
} catch(StoreException e) {
throw new AccountException(e.toString());
}
logmsg("createAccount: Added account to Store");
logmsg("createAccount -> " + accId);
return accId;
}
/**
* Cause the account to be stored and saved in its current state.
*/
protected void storeAccount(Account account)
throws AccountException
{
try {
accountStore.addAccount(account);
} catch (StoreException ex) {
ex.printStackTrace();
throw new AccountException(ex.toString());
}
}
/**
* Rename the account.
*/
public void renameAccount(AccountId accountid, String name)
throws AccountException
{
Account account = getKnownAccount(accountid);
account.setName(name);
storeAccount(account);
}
/**
* Remove the account out of the account store.
* Only possible if it is empty of subaccounts.
*
* @except AccountException if the account is unknown, or has subaccounts
*/
public void removeAccount(AccountId accountid)
throws AccountException
{
Account account = getKnownAccount(accountid);
//
// This is a bit grotty.
// The account is spread across the Account, SubAccounts, Receipts.
//
SubAccount[] subs = account.getSubAccounts();
if (subs.length > 0)
{
throw new AccountException("ac has subs");
}
try {
accountStore.deleteAccount(account);
} catch (StoreException ex) {
throw new AccountException("ac not deleted: " + ex);
}
}
/**
* The account and subAccount have to exist.
*
* @except AccountException if the sub could not be frozen or ac not there
*/
public void freezeSubAccount(AccountId accountid, ItemId item)
throws AccountException
{
Account account = getKnownAccount(accountid);
SubAccount sub = account.getSub(item);
if (sub == null)
throw new AccountException("sub not known");
sub.freeze();
storeAccount(account);
}
/**
* The account has to exist, the subaccount may not.
* Before calling this, freeze the subaccount.
*
* @except AccountException if the sub not there or not frozen
*/
public void removeSubAccount(AccountId accountid, ItemId item)
throws AccountException
{
Account account = getKnownAccount(accountid);
SubAccount sub = account.getSub(item);
if (sub == null)
throw new AccountException("sub not known");
if (!sub.isFrozen())
throw new AccountException("sub not frozen");
// receipts are not lost by this mechanism, which makes
// it silly - how to see the receipts?
account.removeSubAccount(item);
storeAccount(account);
}
/**
* Adds the Value subaccount to the Account.
*
*/
public void addValueAccount(AccountId accountid, ItemId item)
throws AccountException
{
Account account = getKnownAccount(accountid);
//
// First, create the subaccount.
// Then, add the account back into the store and hope it saves.
//
SubAccount sub = account.getSub(item);
if (sub != null)
{
throw new AccountException("already? : " + sub);
}
sub = new ValueAccount(item);
addSubAccount(account, sub);
}
/**
* Adds the subaccount to the Account.
*
*/
protected void addSubAccount(Account acct, SubAccount sub)
throws AccountException
{
//
// Ouch! This part should be SYNCronised, as it can
// trigger slowdowns and the user can call in here
// many times...
//
try {
acct.newSub(sub);
} catch (SOXSubAccountException ex) {
ex.printStackTrace();
throw new AccountException("EX?: " + ex);
} catch (SOXAccountException ex) {
ex.printStackTrace();
throw new AccountException("EX?: " + ex);
}
storeAccount(acct);
//
// Quick check that it is there.
//
Account account = getAccountOrDie(acct.getId());
ItemId item = sub.getItemId();
ItemId[] items = account.getItemIds();
logmsg("#items: " + items.length);
boolean found = false;
for (int i = 0; i < items.length; i++)
{
if (items[i].equals(item))
found = true;
logmsg("item: " + items[i]);
}
if (!found)
throw new Panic("could not add sub to " + acct + ": " + sub);
}
///////////// U P D A T E /////////////////////////////
/**
* Make an AccountId for this sub account.
* Gets the account, then the id.
* The address bits are unset.
*/
protected AccountId getAccountId(SubAccount sub)
{
Account ac = sub.getAccount();
return ac.getId();
}
/**
* Get and sign for the mail for the item's server for this account.
* Will drag in all items that are handled by this account at
* that SOX Server - part of the account request structure.
*
* The account and sub account must exist.
*
* @throw AccountException if something doesn't exist
*/
public void update(AccountId accountid, ItemId item)
throws AccountException
{
logmsg("AccountId update");
SubAccount sub = getKnownSubAccount(accountid, item);
//
// Do the sub.update, which sends all cached hashes,
// and returns more receipts.
//
try
{
manyUpdates(sub, null); // sub is surrogate for account
} catch (SOXException e) {
e.printStackTrace();
logmsg("update failed: " + e);
}
}
/**
* Handle these mails that came via another request
* or another subaccount.
* The subaccount must identify the account and the SOXServer.
* All mail items must come from the same SOXServer,
* (but are not necessarily the same subaccount, etc).
* This points to a flaw in the sox object model.
*/
protected void internalUpdate(SubAccount sub, MailItem[] mails)
{
try {
manyUpdates(sub, mails);
} catch (SOXLaterException ex) {
logmsg("try mail later!");
} catch (SOXException ex) {
logmsg("SOXEx: " + ex);
}
}
protected void internalUpdate(SubAccount sub, MailItem mail)
{
MailItem[] mm = new MailItem[1];
mm[0] = mail;
internalUpdate(sub, mm);
}
/**
* Get and sign for the mail from the issuer for this account,
* and handle these mails also.
* If any new mail is returned in the process, deal with that
* and sign for it as well, cyclically.
*/
protected void manyUpdates(SubAccount sub, MailItem[] mails)
throws SOXLaterException, SOXException
{
MailId[] confirms = null;
logmsg("manyUpdates(" + ((mails == null)? 0: mails.length) + ")");
if (mails != null && (mails.length > 0))
confirms = tryHandle(sub, mails);
//
// Spin around getting and signing for mail.
// Would often be two cycles, might be 3 if
// something arrived during the process.
// Too many cycles would probably indicate a bug
// somewhere. Although, if someone does a whole
// bunch of transactions, this can cause a steady
// series of cycles. Better to back off in that
// case. (Like if WebFunds does a hundred cancels...)
//
for (int i = 0; i < 10; i++)
{
// mails = tryUpdate(sub, confirms);
mails = sub.update(confirms);
if (mails == null || (mails.length == 0))
return ;
logmsg("for(;" + i + "<4; ++) on " + mails.length + " mails");
//
// Did I get some more mail in that update?
// Sign for it now ... Actually, this is necessary
// as there is nowhere else to save the confirms.
// Can't save it in the sub account as that is only
// a copy...
//
confirms = tryHandle(sub, mails);
if (confirms == null || (confirms.length == 0))
return ;
}
logmsg("too many: mail appears to be spinning");
return ;
}
/**
* Do an update - one single cycle of request with sigs.
*/
protected MailId[] tryHandle(SubAccount sub, MailItem[] mails)
{
Vector newConfirms = new Vector();
//
// Have received some more mail,
// save them and ready them for confirm
//
logmsg("tryHandle() received " + mails.length + " mail items");
for (int i = 0; i < mails.length; i++)
{
MailItem mail = mails[i];
if (handleMail(mail, sub)) // handle mail, and
{
MailId id = mail.getMailId(); // confirm this number
newConfirms.addElement(id); // confirm
logmsg("uccessfully saved " + id);
}
}
MailId[] confirms = new MailId[newConfirms.size()];
newConfirms.copyInto(confirms);
logmsg("have " + confirms.length + " MailIds ready to confirm for " +
sub.fp());
return confirms ;
}
/**
* Switchboard for various mail types.
* Currently, we only handle Receipts here.
* To handle other types, override this method.
*
* @return true if it is saved *and* can now be confirmed.
* should really be ternary: {don't know, failed to save, ok}
* so we can extend, and call super() on this method.
* throw a save-fail exception?
*/
protected boolean handleMail(MailItem mail, SubAccount sub)
{
boolean ok = false;
if (mail.isReceipt())
ok = handleReceiptData(mail.getMessage(), getAccountId(sub));
else
logmsg("Unknown mail: " + mail.getType() + " ... ignoring:\n\n" +
Hex.data2hex(mail.getMail()) + "\n\n");
return ok ;
}
/**
* For some reason, Receipts are stored on an account basis.
*/
protected boolean handleReceiptData(byte[] recdata, AccountId acct)
{
Receipt rec = null;
try {
rec = new Receipt(recdata);
} catch (SOXPacketException ex) {
// SEPs: ex.printStackTrace(err());
logmsg("not a receipt: " + ex);
logmsg(Hex.data2hex(recdata));
return false ;
}
logmsg("handleReceiptData(): " + rec);
return handleReceipt(rec, acct);
}
protected boolean handleReceipt(Receipt rec, AccountId acct)
{
try {
receiptStore.addReceipt(rec, acct);
} catch (StoreException ex) {
ex.printStackTrace();
logmsg("losing receipts for " + acct.fp() + ": " + ex);
return false ; // must not confirm !!!!
}
return true ; // got it stored, please confirm
}
protected void confirm(Vector v, byte[] b)
{
v.addElement(MailId.newInstance(b));
}
protected void confirm(Vector v, MailItem mi)
{
v.addElement(mi.getMailId());
}
//////////////// Transactions ///////////////////////////////
/**
*
* @return a list of Receipt within this subaccount
*/
public Receipt[] getReceipts(AccountId acct, ItemId item)
throws AccountException
{
Receipt[] receipts;
try
{
receipts = receiptStore.getReceipts(acct, item);
}
catch (StoreException ex)
{
throw new AccountException("no pendings: " + ex);
}
return receipts;
}
/**
*
* @return a list of Pendings within this subaccount
*/
public PendingReceipt[] getPending(AccountId acct, ItemId item)
throws AccountException
{
PendingReceipt[] pendings;
try
{
pendings = receiptStore.getPendingReceipts(acct, item);
}
catch (StoreException ex)
{
throw new AccountException("no pendings: " + ex);
}
return pendings;
}
public String toString()
{
return shortname;
}
}