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 javax.microedition.midlet.MIDlet;
030: import com.sun.midp.midlet.*;
031: import com.sun.midp.midletsuite.*;
032:
033: import java.io.IOException;
034: import java.util.Random;
035:
036: import javax.microedition.payment.TransactionListener;
037: import javax.microedition.payment.TransactionRecord;
038: import javax.microedition.payment.TransactionModuleException;
039: import javax.microedition.payment.TransactionFeatureException;
040: import javax.microedition.payment.TransactionListenerException;
041: import javax.microedition.payment.TransactionPayloadException;
042:
043: import com.sun.midp.security.Permissions;
044: import com.sun.midp.util.Properties;
045: import com.sun.midp.midletsuite.MIDletSuiteStorage;
046:
047: import java.io.InputStream;
048: import javax.microedition.rms.RecordStoreException;
049: import com.sun.midp.log.Logging;
050: import java.io.ByteArrayInputStream;
051: import javax.microedition.rms.RecordStoreNotFoundException;
052: import javax.microedition.rms.RecordStore;
053: import com.sun.midp.installer.JadProperties;
054: import com.sun.midp.log.LogChannels;
055: import java.io.ByteArrayOutputStream;
056: import java.io.OutputStreamWriter;
057:
058: /**
059: * This class implements parts of <code>TransactionModuleImpl</code> which
060: * are dependent on the CLDC. It's created by a factory method in the payment
061: * module.
062: *
063: * @version
064: * @see CldcPaymentModule#createTransactionModule
065: */
066: public class CldcTransactionModuleImpl extends TransactionModuleImpl {
067:
068: /** MDIletSuite being run */
069: private MIDletSuite midletSuite;
070:
071: /**
072: * The name of the file where the Midlet Payment Update Info is stored.
073: */
074: private static final String PAYMENT_UPDATE_FILE_NAME = "payment_update";
075:
076: /**
077: * MIDlet property for the Payment Version.
078: */
079: public static final String PAY_VERSION_PROP = "Pay-Version";
080:
081: /** MIDletSuite payment info */
082: private PaymentInfo paymentInfo = null;
083:
084: /**
085: * Creates a new instance of <code>CldcTransactionModuleImpl</code>.
086: * Requires a reference to the application MIDlet which initiated the
087: * payment.
088: * <p>
089: * Handles the <code>Pay-Debug-FailInitialize</code> debug mode.
090: *
091: * @param object the caller MIDlet
092: * @throws TransactionModuleException indicates an error preventing the
093: * MIDlet from using the payment API
094: * @see javax.microedition.payment.TransactionModule#TransactionModule
095: */
096: protected CldcTransactionModuleImpl(Object object)
097: throws TransactionModuleException {
098: super (object);
099:
100: PaymentInfo paymentInfo = getPaymentInfo();
101: if (paymentInfo == null) {
102: throw new TransactionModuleException(
103: "Missing provisioning " + "information");
104: }
105:
106: /* Check if MIDletSuite is Trusted */
107: // Removed - SecurityException is thrown if untrusted MIDlet tries to use
108: // restricted permission, so no additional check is required
109: // if (!paymentInfo.isDemoMode() && !getMIDletSuite().isTrusted()) {
110: // throw new TransactionModuleException("MidletSuite is untrusted");
111: // }
112: // === DEBUG MODE ===
113: // (PaymentModule.random.nextInt(192) < 64) = approx. one in three will
114: // fail in this way
115: if (paymentInfo.isDemoMode()
116: && (paymentInfo.getDbgFailInitialize() || paymentInfo
117: .getDbgRandomTests()
118: && (CldcPaymentModule.random.nextInt(192) < 64))) {
119: throw new TransactionModuleException("Debug mode");
120: }
121: // === DEBUG MODE ===
122: }
123:
124: /**
125: * Returns the MIDlet suite of the MIDlet.
126: *
127: * @return the MIDlet suite
128: */
129: private MIDletSuite getMIDletSuite() {
130: if (midletSuite == null) {
131: midletSuite = (MIDletSuite) Scheduler.getScheduler()
132: .getMIDletSuite();
133: }
134: return midletSuite;
135: }
136:
137: /**
138: * Ensures that the MIDlet will have required privileges to do a protected
139: * operation.
140: *
141: * @param permission the required permission
142: * @param name an additional info string
143: * @throws SecurityException if the permission is not granted
144: * @throws InterruptedException if the thread waiting for the permission
145: * is interrupted
146: */
147: protected void checkForPermission(int permission, String name)
148: throws InterruptedException {
149: getMIDletSuite().checkForPermission(permission, name);
150: }
151:
152: /**
153: * Helper class that allows PaymentInfo to be created from MIDletSuiet
154: * properies
155: */
156: class PropertiesWraper extends Properties {
157: /** Provision info source */
158: private MIDletSuite midletSuite;
159:
160: /**
161: * PropertiesWraper constructor
162: *
163: * @param suite property storage
164: */
165: public PropertiesWraper(MIDletSuite suite) {
166: midletSuite = suite;
167: }
168:
169: /**
170: * Returns property from given MIDletSuite
171: *
172: * @param key property name
173: * @return property value or null
174: */
175: public String getProperty(String key) {
176: return midletSuite.getProperty(key);
177: }
178: }
179:
180: /**
181: * Returns the payment provisioning information associated with the MIDlet.
182: *
183: * @return the payment provisioning information
184: */
185: protected PaymentInfo getPaymentInfo() {
186: if (paymentInfo == null) {
187: if (getMIDletSuite().getProperty(PAY_VERSION_PROP) == null) {
188: // quick check
189: return null;
190: }
191: RecordStore store;
192: PropertiesWraper props = new PropertiesWraper(
193: getMIDletSuite());
194: // try to read updated provision info
195: try {
196: store = RecordStore.openRecordStore(
197: PAYMENT_UPDATE_FILE_NAME, false);
198: try {
199: byte[] data = new byte[store.getRecordSize(1)];
200: data = store.getRecord(1);
201:
202: InputStream bis = new ByteArrayInputStream(data);
203: JadProperties payProps = new JadProperties();
204: payProps.load(bis);
205: bis.close();
206:
207: paymentInfo = PaymentInfo.createFromProperties(
208: props, payProps);
209: } finally {
210: store.closeRecordStore();
211: }
212: } catch (RecordStoreNotFoundException ex) {
213:
214: // if the is no update file try to read payment info from
215: // suite properties
216: try {
217: paymentInfo = PaymentInfo.createFromProperties(
218: props, props);
219: } catch (PaymentException e) {
220: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
221: Logging.report(Logging.ERROR,
222: LogChannels.LC_AMS,
223: "getPaymentInfo threw an PaymentException: "
224: + e.getMessage());
225: }
226: }
227: } catch (RecordStoreException ex) {
228: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
229: Logging.report(Logging.ERROR, LogChannels.LC_AMS,
230: "getPaymentInfo threw an RecordStoreException: "
231: + ex.getMessage());
232: }
233: } catch (PaymentException ex) {
234: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
235: Logging.report(Logging.ERROR, LogChannels.LC_AMS,
236: "getPaymentInfo threw an PaymentException: "
237: + ex.getMessage());
238: }
239: } catch (IOException ex) {
240: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
241: Logging.report(Logging.ERROR, LogChannels.LC_AMS,
242: "getPaymentInfo threw an IOException: "
243: + ex.getMessage());
244: }
245: }
246: if (paymentInfo != null) {
247: // initialize the transaction store for this MIDletSuite only
248: // for once
249: ((CldcPaymentModule) PaymentModule.getInstance())
250: .initializeTransactionStore(
251: paymentInfo,
252: midletSuite.getID(),
253: midletSuite
254: .getProperty(MIDletSuiteImpl.SUITE_NAME_PROP));
255: }
256: }
257: return paymentInfo;
258: }
259:
260: /**
261: * Stores the payment provisioning information associated with the MIDlet.
262: *
263: * @throws IOException indicates an output error
264: */
265: protected void savePaymentInfo() throws IOException {
266: PaymentInfo pInfo = getPaymentInfo();
267:
268: if (pInfo != null) {
269: ByteArrayOutputStream bos = new ByteArrayOutputStream();
270: OutputStreamWriter os = new OutputStreamWriter(bos, "UTF-8");
271: byte[] data;
272:
273: try {
274: pInfo.export(os);
275: data = bos.toByteArray();
276: } finally {
277: os.close();
278: }
279:
280: RecordStore store = null;
281: try {
282: store = RecordStore.openRecordStore(
283: PAYMENT_UPDATE_FILE_NAME, true);
284: try {
285: if (store.getNumRecords() == 0) {
286: /* Record Store is EMPTY - add new record */
287: store.addRecord(data, 0, data.length);
288: } else {
289: /* Replace first record */
290: store.setRecord(1, data, 0, data.length);
291: }
292: } finally {
293: store.closeRecordStore();
294: }
295: } catch (RecordStoreException e) {
296: throw new IOException(
297: "Storage Failure: Can not store Payment Info");
298: }
299: }
300:
301: }
302:
303: /**
304: * Returns the MIDlet payment ID that can be used to store transaction
305: * records for the MIDlet initiated transactions into the transaction store.
306: *
307: * @return the ID
308: */
309: protected int getApplicationID() {
310: return ((CldcPaymentModule) PaymentModule.getInstance())
311: .getPaymentID(getMIDletSuite().getID());
312: }
313:
314: /**
315: * Returns an array of the missed transactions associated with the MIDlet
316: * suite.
317: * <p>
318: * It handles the <code>Pay-Debug-MissedTransactions</code> debug mode.
319: *
320: * @return an array of the missed transactions
321: */
322: protected TransactionRecord[] getMissedTransactions() {
323: // === DEBUG MODE ===
324: PaymentInfo paymentInfo = getPaymentInfo();
325: if (paymentInfo.isDemoMode()
326: && paymentInfo.getDbgMissedTransactions() >= 0) {
327: int applicationID = getApplicationID();
328:
329: CldcPaymentModule paymentModule = (CldcPaymentModule) PaymentModule
330: .getInstance();
331: CldcTransactionStoreImpl transactionStore = (CldcTransactionStoreImpl) paymentModule
332: .getTransactionStore();
333: TransactionRecord[] fakeMissed = null;
334:
335: try {
336: fakeMissed = transactionStore.getMissedTransactions(
337: applicationID, true);
338: } catch (IOException e) {
339: }
340:
341: return fakeMissed;
342: }
343: // === DEBUG MODE ===
344: return super.getMissedTransactions();
345: }
346: }
|