001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.j2me.payment;
028:
029: import java.util.Vector;
030:
031: import java.io.IOException;
032: import java.io.DataInputStream;
033: import java.io.DataOutputStream;
034: import java.io.ByteArrayOutputStream;
035:
036: import javax.microedition.payment.TransactionRecord;
037:
038: import com.sun.j2me.payment.Transaction;
039: import com.sun.j2me.payment.ProviderInfo;
040:
041: /**
042: * This class implements <code>TransactionRecord</code>.
043: *
044: * @version 1.4
045: * @see javax.microedition.payment.TransactionRecord
046: */
047: final class CldcTransactionRecordImpl implements TransactionRecord {
048:
049: /** Status of Transaction - Reserved */
050: static final int RESERVED = 0;
051: /** Status of Transaction - Missed */
052: static final int MISSED = 1;
053: /** Status of Transaction - Passed */
054: static final int PASSED = 2;
055:
056: /** Transaction ID */
057: private int transactionID;
058: /** Application ID */
059: private int applicationID;
060: /** Application Name */
061: private String applicationName;
062: /** Feature ID */
063: private int featureID;
064: /** Feature Title */
065: private String featureTitle;
066: /** price */
067: private double price;
068: /** currency */
069: private String currency;
070: /** state of Transaction */
071: private int state;
072: /** Timestamp of Transaction */
073: private long timestamp;
074: /** Transaction status - Reserved/Missed/Passed */
075: private int status;
076: /** Fake Transaction */
077: private boolean fake;
078:
079: /** record ID */
080: private int recordID;
081:
082: /** empty currency */
083: private static final String EMPTY_CURRENCY = "";
084: /** USD currency */
085: private static final String TEMPLATE_CURRENCY = "USD";
086:
087: /**
088: * Creates an instance of the <code>CldcTransactionRecordImpl</code> class
089: * from the given application ID, name of the MIDlet suite, transaction,
090: * the timestamp and the fake flag. The new transaction record has its
091: * <code>wasMissed</code> flag set.
092: *
093: * @param applicationID the application ID
094: * @param applicationName the application name
095: * @param transaction the transaction
096: * @param timestamp the timestamp
097: * @param fake the fake flag
098: */
099: CldcTransactionRecordImpl(int applicationID,
100: String applicationName, Transaction transaction,
101: long timestamp, boolean fake) {
102: this .applicationID = applicationID;
103: this .applicationName = applicationName;
104:
105: transactionID = transaction.getTransactionID();
106: featureID = transaction.getFeatureID();
107: featureTitle = transaction.getFeatureTitle();
108: price = transaction.getPrice();
109: currency = transaction.getCurrency();
110:
111: if (currency == null) {
112: currency = EMPTY_CURRENCY;
113: }
114:
115: switch (transaction.getState()) {
116: case Transaction.SUCCESSFUL:
117: state = TransactionRecord.TRANSACTION_SUCCESSFUL;
118: break;
119: case Transaction.REJECTED:
120: state = TransactionRecord.TRANSACTION_REJECTED;
121: break;
122: case Transaction.FAILED:
123: state = TransactionRecord.TRANSACTION_FAILED;
124: break;
125: }
126:
127: this .timestamp = timestamp;
128: this .fake = fake;
129: this .status = RESERVED;
130:
131: this .recordID = 0;
132: }
133:
134: /**
135: * Returns the exact duplicate of this transaction record except of the
136: * <code>wasMissed</code> flag which is set to the value of the input
137: * parameter.
138: *
139: * @return the new duplicate transaction record
140: */
141: CldcTransactionRecordImpl createDuplicateRecord() {
142: CldcTransactionRecordImpl newRecord = new CldcTransactionRecordImpl();
143:
144: newRecord.transactionID = transactionID;
145: newRecord.applicationID = applicationID;
146: newRecord.applicationName = applicationName;
147: newRecord.featureID = featureID;
148: newRecord.featureTitle = featureTitle;
149: newRecord.price = price;
150: newRecord.currency = currency;
151: newRecord.state = state;
152: newRecord.timestamp = timestamp;
153: newRecord.fake = fake;
154: newRecord.recordID = recordID;
155:
156: newRecord.status = PASSED;
157:
158: return newRecord;
159: }
160:
161: /**
162: * Returns the feature ID.
163: *
164: * @return the feature ID
165: */
166: public int getFeatureID() {
167: return featureID;
168: }
169:
170: /**
171: * Returns the timestamp when the transaction was finished.
172: *
173: * @return the timestamp
174: */
175: public long getFinishedTimestamp() {
176: return timestamp;
177: }
178:
179: /**
180: * Returns the final state of the transaction. It can be one of
181: * <code>TRANSACTION_SUCCESSFUL</code>, <code>TRANSACTION_FAILED</code>,
182: * <code>TRANSACTION_REJECTED</code>.
183: *
184: * @return the final state
185: * @see javax.microedition.payment.TransactionRecord#TRANSACTION_SUCCESSFUL
186: * @see javax.microedition.payment.TransactionRecord#TRANSACTION_FAILED
187: * @see javax.microedition.payment.TransactionRecord#TRANSACTION_REJECTED
188: */
189: public int getState() {
190: return state;
191: }
192:
193: /**
194: * Sets the state of the transaction. It can be one of
195: * <code>TRANSACTION_SUCCESSFUL</code>, <code>TRANSACTION_FAILED</code>,
196: * <code>TRANSACTION_REJECTED</code>.
197: *
198: * @param transaction the transaction
199: */
200: void update(Transaction transaction) {
201: timestamp = System.currentTimeMillis();
202: switch (transaction.getState()) {
203: case Transaction.SUCCESSFUL:
204: state = TransactionRecord.TRANSACTION_SUCCESSFUL;
205: break;
206: case Transaction.REJECTED:
207: state = TransactionRecord.TRANSACTION_REJECTED;
208: break;
209: case Transaction.FAILED:
210: state = TransactionRecord.TRANSACTION_FAILED;
211: break;
212: }
213: }
214:
215: /**
216: * Returns the transaction ID of the transaction.
217: *
218: * @return the transaction ID
219: */
220: public int getTransactionID() {
221: return transactionID;
222: }
223:
224: /**
225: * Indicates if the MIDlet have or haven't been notified about the final
226: * state of the transaction.
227: *
228: * @return <code>true</code> if the MIDlet haven't been notified yet
229: */
230: public boolean wasMissed() {
231: return (status != PASSED);
232: }
233:
234: /**
235: * Indicates status of Transaction:
236: * RESERVED/MISSED/PASSED
237: * @return RESERVED if Transaction is started
238: * MISSED if confirmation is received
239: * PASSED if user was notifyed
240: */
241: public int getStatus() {
242: return status;
243: }
244:
245: /**
246: * Change status of Transaction:
247: * RESERVED/MISSED/PASSED
248: * @param status of Transaction
249: */
250: void setStatus(int status) {
251: if ((status == MISSED) || (status == PASSED)) {
252: this .status = status;
253: }
254: }
255:
256: /**
257: * Returns the appplication ID of the application (MIDlet) which initiated
258: * the transaction.
259: *
260: * @return the application ID
261: * @see com.sun.j2me.payment.TransactionStore#getNextApplicationID
262: */
263: public int getApplicationID() {
264: return applicationID;
265: }
266:
267: /**
268: * Returns the name of the application (MIDlet) which initiated the
269: * transaction.
270: *
271: * @return the application name
272: */
273: public String getApplicationName() {
274: return applicationName;
275: }
276:
277: /**
278: * Returns the title of the feature.
279: *
280: * @return the feature title
281: */
282: public String getFeatureTitle() {
283: return featureTitle;
284: }
285:
286: /**
287: * Returns the price of the feature or <code>0</code> if it hasn't been
288: * set when the transaction ended.
289: *
290: * @return the price
291: */
292: public double getPrice() {
293: return price;
294: }
295:
296: /**
297: * Returns the currency of the price or an empty string if it hasn't been
298: * set when the transaction ended.
299: *
300: * @return the currency code
301: */
302: public String getCurrency() {
303: return currency;
304: }
305:
306: /**
307: * Indicates if the current transaction record is fake. A fake record isn't
308: * permanently stored into the transaction store file. Fake records are
309: * produced in the debug mode and by MIDlet suites which are run without
310: * beeing installed.
311: *
312: * @return <code>true</code> if the record is fake
313: */
314: public boolean isFake() {
315: return fake;
316: }
317:
318: /**
319: * Returns the size (in bytes) which is needed to serialize the transaction
320: * record.
321: *
322: * @return the size of the serialized transaction record
323: */
324: int getSerializedSize() {
325: if (fake) {
326: return 0;
327: }
328:
329: int size = 0;
330: try {
331: size = calculateSize(applicationName, featureTitle);
332: } catch (IOException e) {
333: }
334:
335: return size;
336: }
337:
338: /**
339: * Constructs empty instance
340: */
341: private CldcTransactionRecordImpl() {
342: }
343:
344: // === DEBUG MODE ===
345: /**
346: * Creates a fake transaction record from the given information. It's
347: * used in the debug mode.
348: *
349: * @param transactionID unique ID of transaction
350: * @param applicationID the application ID
351: * @param applicationName the application name
352: * @param featureID the feature ID
353: * @param featureTitle the title of the feature
354: * @param price the price of the feature
355: * @param currency the currency to pay
356: * @param state the state of the transaction
357: * @param timestamp the timestamp
358: *
359: * @return new instance of CldcTransactionRecordImpl
360: */
361: static CldcTransactionRecordImpl createFakeRecord(
362: int transactionID, int applicationID,
363: String applicationName, int featureID, String featureTitle,
364: double price, String currency, int state, long timestamp) {
365: CldcTransactionRecordImpl record = new CldcTransactionRecordImpl();
366:
367: record.transactionID = transactionID;
368: record.applicationID = applicationID;
369: record.applicationName = applicationName;
370: record.featureID = featureID;
371: record.featureTitle = featureTitle;
372: record.price = price;
373: record.currency = currency;
374: record.state = state;
375: record.timestamp = timestamp;
376:
377: record.status = RESERVED;
378: record.fake = true;
379:
380: record.recordID = 0;
381:
382: return record;
383: }
384:
385: // === DEBUG MODE ===
386:
387: /**
388: * Creates a new transaction record from the data read from the given input
389: * stream.
390: *
391: * @param is the input stream
392: * @return the new transaction record
393: * @throws IOException indicates a reading failure
394: */
395: static CldcTransactionRecordImpl read(DataInputStream is)
396: throws IOException {
397: CldcTransactionRecordImpl newRecord = new CldcTransactionRecordImpl();
398:
399: newRecord.transactionID = is.readInt();
400: newRecord.applicationID = is.readInt();
401: newRecord.applicationName = is.readUTF();
402: newRecord.featureID = is.readInt();
403: newRecord.featureTitle = is.readUTF();
404: newRecord.price = is.readDouble();
405: newRecord.currency = is.readUTF();
406: newRecord.state = is.readInt();
407: newRecord.timestamp = is.readLong();
408: newRecord.status = is.readInt();
409:
410: return newRecord;
411: }
412:
413: /**
414: * Stores the transaction record into the given output stream.
415: *
416: * @param os the output stream
417: * @throws IOException indicates a writing failure
418: */
419: void write(DataOutputStream os) throws IOException {
420: os.writeInt(transactionID);
421: os.writeInt(applicationID);
422: os.writeUTF(applicationName);
423: os.writeInt(featureID);
424: os.writeUTF(featureTitle);
425: os.writeDouble(price);
426: os.writeUTF(currency);
427: os.writeInt(state);
428: os.writeLong(timestamp);
429: os.writeInt(status);
430: }
431:
432: /**
433: * Calculates the size in bytes which will be needed to store a transaction
434: * record with the specified application name and feature title.
435: *
436: * @param applicationName the application name
437: * @param featureTitle the feature title
438: * @return the size needed to store such record
439: */
440: static int calculateSize(String applicationName, String featureTitle)
441: throws IOException {
442: // calculate the required space
443: int size;
444:
445: ByteArrayOutputStream bos = new ByteArrayOutputStream();
446: DataOutputStream os = new DataOutputStream(bos);
447:
448: try {
449: os.writeUTF(applicationName);
450: os.writeUTF(featureTitle);
451: os.writeUTF(TEMPLATE_CURRENCY);
452: size = bos.size();
453:
454: } finally {
455: os.close();
456: }
457:
458: size += 4 + // transactionID
459: 4 + // applicationID
460: 4 + // featureID
461: 8 + // price
462: 4 + // state
463: 8 + // timestamp
464: 4; // status
465:
466: return size;
467: }
468:
469: /**
470: * Stores Record ID for internal usage
471: *
472: * @param recordID unique ID of the Transaction Record
473: */
474: void setRecordID(int recordID) {
475: this .recordID = recordID;
476: }
477:
478: /**
479: * Returns Record ID of the transaction
480: *
481: * @return recordID of the Transaction
482: */
483: int getRecordID() {
484: return recordID;
485: }
486:
487: /**
488: * Adds Transaction Record into the list
489: *
490: * @param v the list of sorted transactions
491: * @return index in the element
492: */
493: int add2list(Vector v) {
494: int count = v.size();
495: if (count == 0) {
496: v.addElement(this );
497: } else {
498: do {
499: if (((CldcTransactionRecordImpl) v.elementAt(count - 1))
500: .getFinishedTimestamp() <= this .timestamp) {
501: v.insertElementAt(this , count);
502: break;
503: }
504: count--;
505: } while (count > 0);
506: if (count == 0) {
507: v.insertElementAt(this, count);
508: }
509: }
510: return count;
511: }
512: }
|