/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.j2me.payment;
import java.io.*;
import java.util.Hashtable;
import java.util.Random;
import java.util.Vector;
import javax.microedition.io.Connector;
import com.sun.midp.io.j2me.storage.*;
import javax.microedition.payment.TransactionRecord;
import com.sun.j2me.payment.PaymentInfo;
import com.sun.j2me.payment.ProviderInfo;
import com.sun.j2me.payment.Transaction;
import com.sun.j2me.payment.TransactionStore;
import com.sun.midp.midletsuite.*;
import com.sun.midp.security.*;
import javax.microedition.rms.*;
/**
* This class implements the transaction store. It uses RMS
* to store the transaction records.
*
*/
public final class CldcTransactionStoreImpl implements TransactionStore {
/**
* This class has a different security domain than the MIDlet suite.
* The token is initialized with a value handed to class constructor.
*/
private static SecurityToken securityToken;
/** The list of Transaction Records with reserved and missed status */
private Vector inProcessRecords = new Vector();
/**
* The reference to the property containing maximum number of passed
* transactions
*/
private static final String TRANSACTIONS_LIMIT_PROPERTY =
"payment.transactions.limit";
/** The maximum number of passed transactions */
private static final int DEFAULT_TRANSACTIONS_LIMIT = 16;
/** The maximum number of missed transactions per application. */
static final int MISSED_TRANSACTIONS_LIMIT = 4;
/** The maximum total number of past transactions. */
static final int PASSED_TRANSACTIONS_LIMIT;
/** The RMS record ID of the NextTransactionID counter */
private static final int NEXT_TRANSACTION_RECORD_ID = 1;
/** The RMS record ID of the NextApplicationD counter */
private static final int NEXT_APPLICATION_RECORD_ID = 2;
/** The number of bytes in the int */
private static final int SIZE_OF_INT = 4;
/** lock used to synchronize this Transaction Store */
Object tsLock;
static {
// get the passed transactions limit from the system
int limit = DEFAULT_TRANSACTIONS_LIMIT;
String limitStr = System.getProperty(TRANSACTIONS_LIMIT_PROPERTY);
if (limitStr != null) {
try {
int value = Integer.parseInt(limitStr);
if (value >= 0) {
limit = value;
}
} catch (NumberFormatException e) {
}
}
PASSED_TRANSACTIONS_LIMIT = limit;
}
/**
* Creates a new instance of <code>CldcTransactionStoreImpl</code>. It needs
* a security token to perform file system manipulations.
*
* @param securityToken the security token
* @throws IOException indicates a storage failure
*/
public CldcTransactionStoreImpl(SecurityToken securityToken)
throws IOException {
this.securityToken = securityToken;
tsLock = new Object();
initStore();
}
/**
* Adds the given transaction to the store. It returns a new transaction
* record for the transaction. This transaction record must have its
* <code>wasMissed</code> flag cleared.
*
* @param transaction the transaction
* @return the new transaction record
* @throws IOException indicates a storage failure
*/
public TransactionRecord addTransaction(
Transaction transaction) throws IOException {
int transactionID = transaction.getTransactionID();
CldcTransactionRecordImpl record = findReservedRecord(transactionID);
if (record == null) {
throw new IllegalStateException("The transaction record hasn't " +
"been reserved");
}
record.setStatus(CldcTransactionRecordImpl.MISSED);
record.update(transaction);
if (!record.isFake()) {
try {
TransactionStorageImpl store =
new TransactionStorageImpl(securityToken, false);
try {
writeTransactionRecord(record, store, record.getRecordID());
} finally {
store.closeStore();
}
} catch (RecordStoreException ex) {
throw new IOException("Storage Failure: " + ex.getMessage());
}
}
// store the transaction record as missed but return it as not missed
return record.createDuplicateRecord();
}
/**
* It returns <code>true</code> if the <code>setDelivered</code> method
* was called for the given transaction ID.
*
* @param transactionID the transaction ID
* @return true if transaction with Transaction ID were delivered to user
* @throws IOException indicates a storage failure
*/
public boolean wasDelivered(int transactionID)
throws IOException {
CldcTransactionRecordImpl transactionRecord = findMissedRecord(
transactionID);
return transactionRecord == null;
}
/**
* This method is called after the application is successfully notified
* about the transaction with the given transaction ID.
*
* @param transactionID the transaction ID
* @throws IOException indicates a storage failure
*/
public void setDelivered(int transactionID)
throws IOException {
CldcTransactionRecordImpl transactionRecord = null;
synchronized (tsLock) {
transactionRecord = findMissedRecord(transactionID);
if (transactionRecord == null) {
throw new IllegalArgumentException("Not a stored transaction");
}
inProcessRecords.removeElement(transactionRecord);
}
if (!transactionRecord.isFake()) {
transactionRecord.setStatus(CldcTransactionRecordImpl.PASSED);
try {
TransactionStorageImpl store =
new TransactionStorageImpl(securityToken, false);
try {
if (transactionRecord.getRecordID() > 0) {
writeTransactionRecord(transactionRecord,
store, transactionRecord.getRecordID());
}
// limit the passed transactions count
limitPassedRecords(store);
} finally {
store.closeStore();
}
} catch (RecordStoreException ex) {
throw new IOException("Storage Failure " + ex.getMessage());
}
}
}
/**
* Returns an identification number, which can be used as
* <code>applicationID</code> in the other methods. During installation
* each payment supporting <code>MIDletSuite</code> should get such number
* and have it stored. From that point this number will identify that
* <code>MIDletSuite</code> to the transaction store.
*
* @return the payment application id
* @throws IOException indicates a storage failure
*/
public int getNextApplicationID() throws IOException {
int nextApplicationID = 0;
try {
TransactionStorageImpl store =
new TransactionStorageImpl(securityToken, false);
try {
nextApplicationID = getIntFromByteArray(
store.getRecord(NEXT_APPLICATION_RECORD_ID));
nextApplicationID++;
if (nextApplicationID <= 0) {
nextApplicationID = 1;
}
store.setRecord(NEXT_APPLICATION_RECORD_ID,
getByteArrayFromInt(nextApplicationID));
} finally {
store.closeStore();
}
} catch (RecordStoreException ex) {
throw new IOException("Storage Failure");
}
return nextApplicationID;
}
/**
* Returns an array of the missed transaction records for the given
* application ID. The transaction records are returned in the order in
* which they have been added to the store. Each transaction record must
* have its <code>wasMissed</code> flag set.
*
* @param applicationID the application ID
* @return the array of the missed transaction records
* @throws IOException indicates a storage failure
*/
public TransactionRecord[] getMissedTransactions(
int applicationID) throws IOException {
return getMissedTransactions(applicationID, false);
}
/**
* Returns a Vector of passed or missed transaction records for the given
* application ID.
*
* @param applicationID the application ID
* @param wasMissed true if missed transactions are required
*
* @return the Vector of transaction records
* @throws IOException indicates a storage failure
*/
private Vector getTransactions(int applicationID, boolean wasMissed)
throws IOException {
Vector records = new Vector();
/* Get none-fake elements */
try {
TransactionStorageImpl store =
new TransactionStorageImpl(securityToken, false);
try {
/* read passed transactions */
int[] recordIDs = store.getRecordIDs();
CldcTransactionRecordImpl r;
for (int i = 0; i < recordIDs.length; i++) {
int recId = recordIDs[i];
r = readTransactionRecord(store, recId);
if (null != r &&
(r.getApplicationID() == applicationID) &&
(r.wasMissed() == wasMissed)) {
r.add2list(records);
}
}
} finally {
store.closeStore();
}
} catch (RecordStoreException ex) {
throw new IOException("Storage Failure: " + ex.getMessage());
}
return records;
}
/**
* Returns an array of the past transaction records for the given
* application ID. The transaction record are returned in the reverse order
* as they have been added to the store (most recent first). Each
* transaction record must have its <code>wasMissed</code> flag cleared.
*
* @param applicationID the application ID
* @return the array of the passed transaction records
* @throws IOException indicates a storage failure
*/
public TransactionRecord[] getPassedTransactions(
int applicationID) throws IOException {
Vector passedRecords = getTransactions(applicationID, false);
int count = passedRecords.size();
if (count == 0) {
return null;
}
CldcTransactionRecordImpl[] appRecords =
new CldcTransactionRecordImpl[count];
/* sort passedRecords and store in appRecords (reverse mode) */
for (int i = 0, j = (count - 1); i < count; i++, j--) {
appRecords[j] = (CldcTransactionRecordImpl)
passedRecords.elementAt(i);
}
passedRecords.removeAllElements();
return appRecords;
}
/**
* Reserves space for the given transaction in the store. It should be
* called before any call to the <code>addTransaction</code> method to
* ensure that the <code>addTransaction</code> method won't fail later
* (when it is inappropriate) due to full store. This method can apply some
* store policies, like enforcing a maximum number of missed transactions
* per <code>MIDletSuite</code>.
* <p>
* The <code>applicationID</code> identifies the application (MIDlet suite)
* to the transaction store. MIDlet suites which are run directly can use
* negative application IDs to avoid permanent storing of created
* transaction records.
*
* @param applicationID the application id
* @param transaction the transaction
* @return an unique ID created for the transaction
* @throws IOException indicates that the store is full or won't accept any
* further transaction records from that application
*/
public int reserve(int applicationID, Transaction transaction)
throws IOException {
/* assign transactionID */
int transactionID;
try {
TransactionStorageImpl store =
new TransactionStorageImpl(securityToken, false);
try {
int count = 0;
/* count missed and reserved transactions */
CldcTransactionRecordImpl r;
for (int i = 0; i < inProcessRecords.size(); i++) {
r = (CldcTransactionRecordImpl)
inProcessRecords.elementAt(i);
if (r.wasMissed() &&
(r.getApplicationID() == applicationID)) {
count++;
}
}
/* check the limit */
if (count >= MISSED_TRANSACTIONS_LIMIT) {
throw new IOException("No more space for records");
}
String applicationName = transaction.
getTransactionModule().getMIDlet().
getAppProperty(MIDletSuiteImpl.SUITE_NAME_PROP);
/* assign transactionID */
int nextTransactionID = getIntFromByteArray(
store.getRecord(NEXT_TRANSACTION_RECORD_ID));
transactionID = nextTransactionID++;
if (nextTransactionID <= 0) {
nextTransactionID = 1;
}
transaction.setTransactionID(transactionID);
/* don't reserve space if applicationID < 0 */
if (applicationID > 0) {
/* create reserved transaction */
CldcTransactionRecordImpl newTransactionRecord =
new CldcTransactionRecordImpl(
applicationID, applicationName,
transaction, System.currentTimeMillis(),
false);
store.setRecord(NEXT_TRANSACTION_RECORD_ID,
getByteArrayFromInt(nextTransactionID));
writeTransactionRecord(newTransactionRecord, store, 0);
inProcessRecords.addElement(newTransactionRecord);
} else {
/* create reserved fake transaction */
CldcTransactionRecordImpl newTransactionRecord =
new CldcTransactionRecordImpl(
applicationID, applicationName,
transaction, System.currentTimeMillis(),
true);
inProcessRecords.addElement(newTransactionRecord);
}
} finally {
store.closeStore();
}
} catch (RecordStoreException ex) {
throw new IOException("Storage Failure: " +
ex.getMessage());
}
return transactionID;
}
/**
* Returns the size which is used in the store by the application of the
* given application ID. This size doesn't include the size of the passed
* transactions (it includes only the part of the store which is
* removed/uninstalled by the <code>removeApplicationRecords</code> method).
*
* @param applicationID the application ID
* @return the size used by the application
* @throws IOException indicates a storage failure
*/
public int getSizeUsedByApplication(int applicationID)
throws IOException {
int size = 0;
try {
TransactionStorageImpl store =
new TransactionStorageImpl(securityToken, false);
try {
/* read missed transactions */
int[] recordIDs = store.getRecordIDs();
CldcTransactionRecordImpl r;
for (int i = 0; i < recordIDs.length; i++) {
int recId = recordIDs[i];
r = readTransactionRecord(store, recId);
if (null != r &&
(r.getApplicationID() == applicationID) &&
r.wasMissed()) {
size += store.getRecordSize(recId);
}
}
} finally {
store.closeStore();
}
}
catch (RecordStoreException ex) {
throw new IOException("Storage Failure: " + ex.getMessage());
}
return size;
}
/**
* Removes the missed records used by the application of the given
* application ID. This is to be used, when the MIDlet suite is uninstalled.
*
* @param applicationID the application ID
* @throws IOException indicates a storage failure
*/
public void removeApplicationRecords(int applicationID)
throws IOException {
try {
TransactionStorageImpl store =
new TransactionStorageImpl(securityToken, false);
try {
/* read missed and passed transactions */
int[] recordIDs = store.getRecordIDs();
CldcTransactionRecordImpl r;
for (int i = 0; i < recordIDs.length; i++) {
int recId = recordIDs[i];
r = readTransactionRecord(store, recId);
if (null != r &&
(r.getApplicationID() == applicationID) &&
r.wasMissed()) {
store.deleteRecord(recId);
}
}
} finally {
store.closeStore();
}
} catch (RecordStoreException ex) {
throw new IOException("Storage Failure: " + ex.getMessage());
}
synchronized (tsLock) {
/* count missed and reserved transactions */
CldcTransactionRecordImpl r;
for (int i = (inProcessRecords.size() - 1); i >= 0; i--) {
r = (CldcTransactionRecordImpl)
inProcessRecords.elementAt(i);
if (r.wasMissed() &&
(r.getApplicationID() == applicationID)) {
inProcessRecords.removeElementAt(i);
}
}
}
}
/**
* Removes all transaction records from the store. This is a helper method
* which is used in test suites to get clean state before test execution.
*
* @throws IOException indicates a storage failure
*/
public void cleanUp() throws IOException {
try {
TransactionStorageImpl.deleteStore(securityToken);
} catch (RecordStoreException ex) {
// Nothing to do
}
initStore();
}
/**
* Returns an array of the missed transaction records for the given
* application ID. The transaction records are returned in the order in
* which they have been added to the store. Each transaction record must
* have its <code>wasMissed</code> flag set.
*
* @param applicationID the application ID
* @param fakeOnly if true returns only fake transactions for given
* applicationID
* @return the array of the missed transaction records
* @throws IOException indicates a storage failure
*/
TransactionRecord[] getMissedTransactions(
int applicationID, boolean fakeOnly) throws IOException {
Vector missedRecords = new Vector();
if (!fakeOnly) {
missedRecords = getTransactions(applicationID, true);
}
synchronized (tsLock) {
/* Get FakeOnly elements */
for (int i = 0; i < inProcessRecords.size(); ++i) {
CldcTransactionRecordImpl record =
(CldcTransactionRecordImpl)inProcessRecords.elementAt(i);
if ((record.getApplicationID() == applicationID) &&
record.wasMissed() && record.isFake()) {
record.add2list(missedRecords);
}
}
}
int count = missedRecords.size();
if (count == 0) {
return null;
}
CldcTransactionRecordImpl[] appRecords =
new CldcTransactionRecordImpl[count];
missedRecords.copyInto(appRecords);
return appRecords;
}
// === DEBUG MODE ===
/**
* Generates Fake records for Debug mode
*
* @param applicationID the application ID
* @param applicationName the application Name
* @param paymentInfo properties of the payment module
* @param featurePrefix prefix of the feature
* @param validProviders array of Provider IDs
* @param count number of fake transactions to add
* @throws IOException indicates a storage failure
*/
void generateFakeRecords(int applicationID,
String applicationName, PaymentInfo paymentInfo,
String featurePrefix, int[] validProviders, int count)
throws IOException {
// count > 0
// reserve transaction IDs
int transactionID;
try {
TransactionStorageImpl store =
new TransactionStorageImpl(securityToken, false);
try {
/* assign transactionID */
int nextTransactionID = getIntFromByteArray(
store.getRecord(NEXT_TRANSACTION_RECORD_ID));
transactionID = nextTransactionID;
nextTransactionID += count;
if (nextTransactionID <= 0) {
nextTransactionID = count;
}
store.setRecord(NEXT_TRANSACTION_RECORD_ID,
getByteArrayFromInt(nextTransactionID));
} finally {
store.closeStore();
}
} catch (RecordStoreException ex) {
throw new IOException("Storage Failure: " + ex.getMessage());
}
Random random = new Random();
long timestamp = System.currentTimeMillis() - 3600000;
int deltaTime = 3600000 / count;
int numFeatures = paymentInfo.getNumFeatures();
for (int i = 0; i < count; ++i) {
int state = random.nextInt(3);
int featureID = (i + (count >>> 1)) * (numFeatures - 1) / count;
double price;
String currency;
if (state != TransactionRecord.TRANSACTION_REJECTED) {
int priceTag = paymentInfo.getPriceTagForFeature(featureID);
int providerID = validProviders[
random.nextInt(validProviders.length)];
ProviderInfo providerInfo = paymentInfo.getProvider(providerID);
price = providerInfo.getPrice(priceTag);
currency = providerInfo.getCurrency();
} else {
price = 0;
currency = "";
}
inProcessRecords.addElement(
CldcTransactionRecordImpl.createFakeRecord(
transactionID++, applicationID, applicationName,
featureID, featurePrefix + featureID, price, currency,
state, timestamp));
if (transactionID <= 0) {
transactionID = 1;
}
timestamp += deltaTime;
}
}
// === DEBUG MODE ===
/**
* Finds Transaction Record with given Transaction ID and status
*
* @param transactionID the transaction ID
* @param status the status of transaction
* @return the transaction record if found or null
* @throws IOException indicates a storage failure
*/
private CldcTransactionRecordImpl findRecord(int transactionID, int status)
throws IOException {
CldcTransactionRecordImpl r = null;
CldcTransactionRecordImpl record = null;
synchronized (tsLock) {
int count = inProcessRecords.size();
/* Try to find in the vector */
for (int i = 0; i < count; ++i) {
r = (CldcTransactionRecordImpl)
inProcessRecords.elementAt(i);
if (r.getTransactionID() == transactionID) {
return r;
}
}
}
/* Get none-fake elements */
try {
TransactionStorageImpl store =
new TransactionStorageImpl(securityToken, false);
try {
/* fiind reserved transaction */
int[] recordIDs = store.getRecordIDs();
for (int i = 0; i < recordIDs.length; i++) {
int recId = recordIDs[i];
r = readTransactionRecord(store, recId);
if (null != r &&
(r.getTransactionID() == transactionID) &&
(r.getStatus() == status)) {
r.setRecordID(recId);
record = r;
break;
}
}
} finally {
store.closeStore();
}
} catch (RecordStoreException ex) {
throw new IOException("Storage Failure: " + ex.getMessage());
} catch (IOException ex) {
throw new IOException("Storage Failure: " + ex.getMessage());
}
return record;
}
/**
* Finds missed Transaction Record with the given Transaction ID
*
* @param transactionID the transaction ID
* @return the transaction record if found or null
* @throws IOException indicates a storage failure
*/
private CldcTransactionRecordImpl findMissedRecord(int transactionID)
throws IOException {
return findRecord(transactionID, CldcTransactionRecordImpl.MISSED);
}
/**
* Finds reserved Transaction Record with the given Transaction ID
*
* @param transactionID the transaction ID
* @return the transaction record if found or null
* @throws IOException indicates a storage failure
*/
private CldcTransactionRecordImpl findReservedRecord(int transactionID)
throws IOException {
return findRecord(transactionID, CldcTransactionRecordImpl.RESERVED);
}
/**
* retrives Int from the ByteArray
*
* @param data the array of bytes
* @return integer value converted from byte array or -1
*/
static int getIntFromByteArray(byte[] data) {
if (data.length == SIZE_OF_INT) {
return (((int) (data[0]) << 24) |
((int) (data[1]) << 16) |
((int) (data[2]) << 8) |
(int) (data[3]));
}
return -1;
}
/**
* puts integer into the ByteArray
*
* @param v the integer value
* @return byte array containing the integer value
*/
static byte[] getByteArrayFromInt(int v) {
byte[] data = new byte[SIZE_OF_INT];
data[0] = (byte) ((byte) ((v) >> 24) & 0xFF);
data[1] = (byte) ((byte) ((v) >> 16) & 0xFF);
data[2] = (byte) ((byte) ((v) >> 8) & 0xFF);
data[3] = (byte) ((byte) (v) & 0xFF);
return data;
}
/**
* Initializes the store. It's used when the transaction store file doesn't
* exists.
*
* @throws IOException indicates a storage failure
*/
private void initStore() throws IOException {
int nextTransactionID = 1;
int nextApplicationID = 1;
try {
TransactionStorageImpl store =
new TransactionStorageImpl(securityToken, true);
try {
if (store.getNumRecords() == 0) {
/* Record ID = NEXT_TRANSACTION_RECORD_ID */
store.addRecord(getByteArrayFromInt(nextTransactionID));
/* Record ID = NEXT_APPLICATION_RECORD_ID */
store.addRecord(getByteArrayFromInt(nextApplicationID));
}
} catch (RecordStoreException ex) {
throw new IOException(
"Error initializing Transaction Store: " + ex.getMessage());
} finally {
store.closeStore();
}
} catch (RecordStoreFullException ex) {
throw new IOException("Error initializing Transaction Store: " +
ex.getMessage());
} catch (RecordStoreException ex) {
/* Nothing to do. The store is creating by another isolate */
}
}
/**
* Limits the count of the passed transaction records in the given
* vector to the given value.
*
* @param st TransactionStorageImpl reference
*
* @throws RecordStoreException indicates a storage failure
* @throws IOException indicates a storage failure
*/
private void limitPassedRecords(TransactionStorageImpl st)
throws RecordStoreException, IOException {
Vector passedRecords = new Vector();
CldcTransactionRecordImpl r;
/* read passed transactions */
int[] recordIDs = st.getRecordIDs();
for (int i = 0; i < recordIDs.length; i++) {
int recId = recordIDs[i];
r = readTransactionRecord(st, recId);
if (null != r &&
!r.wasMissed()) {
r.add2list(passedRecords);
}
}
while (passedRecords.size() > PASSED_TRANSACTIONS_LIMIT) {
r = (CldcTransactionRecordImpl) passedRecords.elementAt(0);
st.deleteRecord(r.getRecordID());
passedRecords.removeElementAt(0);
}
}
/**
* Remove missed transaction for give application.
*
* @param appID application payment id
* @throws IOException if there is an error accessing storage
*/
protected void removeMissedTransaction(int appID) throws IOException {
CldcTransactionRecordImpl[] recs =
(CldcTransactionRecordImpl[]) getMissedTransactions(appID);
if (null == recs) {
return;
}
try {
TransactionStorageImpl store =
new TransactionStorageImpl(securityToken, false);
try {
for (int i = 0; i < recs.length; i++) {
store.deleteRecord(recs[i].getRecordID());
}
} finally {
store.closeStore();
}
} catch (RecordStoreException ex) {
throw new IOException("Storage Failure: " + ex.getMessage());
}
}
/**
* This service function reads and returns TransactionRecord from store.
*
* @param st TransactionStorageImpl reference
* @param recId ID of the record to be read
* @return CldcTransactionRecordImpl if recId points to proper record,
* otherwise null
* @throws IOException if an I/O error occurs
* @throws RecordStoreException if a general record store exception occurs
*/
private CldcTransactionRecordImpl readTransactionRecord(
TransactionStorageImpl st, int recId) throws IOException,
RecordStoreException {
byte[] nextRec = st.getRecord(recId);
CldcTransactionRecordImpl r = null;
if (nextRec.length > SIZE_OF_INT) {
ByteArrayInputStream bis =
new ByteArrayInputStream(nextRec);
DataInputStream is = new DataInputStream(bis);
r = CldcTransactionRecordImpl.read(is);
r.setRecordID(recId);
}
return r;
}
/**
* This service function writes TransactionRecord into store.
*
* @param record CldcTransactionRecordImpl reference
* @param st TransactionStorageImpl reference
* @param recId ID of the record to be replaced or 0
*
* @throws IOException if an I/O error occurs
* @throws RecordStoreException if a general record store exception occurs
*/
private void writeTransactionRecord(CldcTransactionRecordImpl record,
TransactionStorageImpl st, int recId) throws IOException,
RecordStoreException {
/* formate transaction inside ByteArray */
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream os = new DataOutputStream(bos);
record.write(os);
byte[] data = bos.toByteArray();
if (recId == 0) {
recId = st.addRecord(data);
record.setRecordID(recId);
} else {
st.setRecord(recId, data);
}
}
}
|