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.Random;
030:
031: import javax.microedition.payment.*;
032: import com.sun.midp.rms.*;
033: import javax.microedition.rms.*;
034: import com.sun.midp.security.*;
035:
036: import com.sun.midp.util.DateParser;
037: import com.sun.midp.io.Util;
038:
039: import java.io.*;
040: import java.util.Hashtable;
041: import java.util.Vector;
042: import javax.microedition.lcdui.*;
043: import javax.microedition.io.Connector;
044: import javax.microedition.io.Connection;
045: import javax.microedition.io.HttpConnection;
046: import javax.microedition.io.ConnectionNotFoundException;
047:
048: import com.sun.midp.lcdui.*;
049: import com.sun.midp.midlet.*;
050: import com.sun.midp.midletsuite.*;
051: import com.sun.midp.i18n.Resource;
052: import com.sun.midp.i18n.ResourceConstants;
053:
054: import com.sun.midp.log.Logging;
055: import com.sun.midp.log.LogChannels;
056: import com.sun.midp.main.MIDletSuiteVerifier;
057:
058: import com.sun.midp.midlet.MIDletSuite;
059:
060: /**
061: * This class extends the <code>PaymentModule</code> class with the device
062: * dependent methods.
063: *
064: * @version
065: */
066: public class CldcPaymentModule extends PaymentModule {
067: /**
068: * Inner class to request security token from SecurityInitializer.
069: * SecurityInitializer should be able to check this inner class name.
070: */
071: static private class SecurityTrusted implements
072: ImplicitlyTrustedClass {
073: };
074:
075: /** This class has a different security domain than the MIDlet suite. */
076: private static SecurityToken classSecurityToken = SecurityInitializer
077: .requestToken(new SecurityTrusted());
078:
079: // === DEBUG MODE ===
080: /** Random generator for debug purpose */
081: static Random random = new Random();
082: // === DEBUG MODE ===
083:
084: /** The name of the file where to store the Midlet PaymentID */
085: private static final String PAYMENT_ID_FILE_NAME = "payment_id";
086:
087: /** Record ID for application payment ID store */
088: private static final int PAYMENT_ID_RECORD = 1;
089:
090: /**
091: * Creates a new instance of CldcPaymentModule.
092: */
093: protected CldcPaymentModule() {
094: }
095:
096: /**
097: * It's a factory method for <code>TransactionModuleImpl</code>.
098: *
099: * @param object the application MIDlet initiating a payment transaction
100: * @return a new instance of a <code>TransactionModuleImpl</code> subclass.
101: * @throws TransactionModuleException indicates a creation failure
102: */
103: public TransactionModuleImpl createTransactionModule(Object object)
104: throws TransactionModuleException {
105: return new CldcTransactionModuleImpl(object);
106: }
107:
108: /**
109: * Initializes the transaction store for the given MIDlet suite.
110: * <p>
111: * Generates fake missed transactions for the
112: * <code>Pay-Debug-MissedTransactions</code> debug mode.
113: *
114: * @param paymentInfo provision information
115: * @param suiteId MDIletSuite ID
116: * @param appName application name
117: */
118: public final void initializeTransactionStore(
119: PaymentInfo paymentInfo, int suiteId, String appName) {
120: // === DEBUG MODE ===
121: int paymentID = getPaymentID(suiteId);
122:
123: int numMissedTransactions = paymentInfo
124: .getDbgMissedTransactions();
125:
126: if (paymentInfo.isDemoMode() && (numMissedTransactions > 0)) {
127: CldcTransactionStoreImpl transactionStore = (CldcTransactionStoreImpl) getTransactionStore();
128: try {
129: transactionStore.generateFakeRecords(paymentID,
130: appName, paymentInfo, "Feature ",
131: getValidProviders(paymentInfo),
132: numMissedTransactions);
133: } catch (IOException e) {
134: // ignore
135: }
136: }
137: // === DEBUG MODE ===
138: }
139:
140: /**
141: * Returns the size the given MIDlet suite uses in the transaction store.
142: * This size doesn't include the size of the passed transactions (it
143: * includes only the part of the store which is removed when the MIDletSuite
144: * is uninstalled).
145: *
146: * @param applicationID the payment application ID of the MIDlet suite
147: * @return the size the MIDlet suite takes in the store
148: */
149: public final int getSizeUsedInStore(int applicationID) {
150: TransactionStore transactionStore = getTransactionStore();
151: int size = 0;
152: try {
153: size = transactionStore
154: .getSizeUsedByApplication(applicationID);
155: } catch (IOException e) {
156: // ignore
157: }
158:
159: return size;
160: }
161:
162: /**
163: * Uninstalls the given MIDlet suite from the transaction store. It means
164: * that the missed transaction records that belong to the suite are removed
165: * from the transaction store.
166: *
167: * @param securityToken a security token with <code>Permissions.AMS</code>
168: * @param applicationID the payment application ID of the MIDlet suite
169: */
170: public final void uninstallFromStore(SecurityToken securityToken,
171: int applicationID) {
172: securityToken.checkIfPermissionAllowed(Permissions.AMS);
173:
174: TransactionStore transactionStore = getTransactionStore();
175: try {
176: transactionStore.removeApplicationRecords(applicationID);
177: } catch (IOException e) {
178: // ignore
179: }
180: }
181:
182: /**
183: * Return missed(pending) transactions headers for given MIdlet suite.
184: *
185: * @param suiteId MIDlet suite ID
186: * @return header of missed records
187: */
188: public final String[] getMissedRecordsHeaders(int suiteId) {
189: CldcTransactionStoreImpl transactionStore = (CldcTransactionStoreImpl) getTransactionStore();
190: CldcTransactionRecordImpl[] recs = null;
191: String[] headers = null;
192: try {
193: int appID = getPaymentID(suiteId);
194: recs = (CldcTransactionRecordImpl[]) transactionStore
195: .getMissedTransactions(appID);
196:
197: // there is no missed transaction
198: if (recs == null) {
199: return null;
200: }
201: headers = new String[recs.length];
202: StringBuffer buff = new StringBuffer();
203: for (int i = 0; i < headers.length; i++) {
204: buff.setLength(0);
205: buff.append(recs[i].getFeatureTitle());
206: buff.append(": ");
207: buff.append(recs[i].getPrice());
208: buff.append(recs[i].getCurrency());
209: headers[i] = buff.toString();
210: }
211: } catch (IOException e) {
212: // skip, return as it is
213: }
214: return headers;
215: }
216:
217: /**
218: * Return application payment ID for given midlet suite.
219: * Create such ID if it is necessary.
220: *
221: * @param suiteId suite ID
222: * @return payment ID
223: */
224: public final int getPaymentID(int suiteId) {
225: RecordStoreImpl store = null;
226: int paymentID = -1;
227: try {
228: store = RecordStoreImpl.openRecordStore(classSecurityToken,
229: suiteId, PAYMENT_ID_FILE_NAME, false);
230: try {
231: byte[] data = new byte[4];
232: data = store.getRecord(1);
233: if (data.length == 4) {
234: paymentID = CldcTransactionStoreImpl
235: .getIntFromByteArray(data);
236: } else {
237: paymentID = -1;
238: }
239: } finally {
240: store.closeRecordStore();
241: }
242: } catch (RecordStoreNotFoundException ex) {
243: try {
244: int appPaymentId = PaymentModule.getInstance()
245: .getNextApplicationID();
246: store = RecordStoreImpl.openRecordStore(
247: classSecurityToken, suiteId,
248: PAYMENT_ID_FILE_NAME, true);
249: try {
250: byte[] data = CldcTransactionStoreImpl
251: .getByteArrayFromInt(appPaymentId);
252: store.addRecord(data, 0, data.length);
253: paymentID = appPaymentId;
254: } finally {
255: store.closeRecordStore();
256: }
257: } catch (RecordStoreException e) {
258: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
259: Logging
260: .report(Logging.ERROR, LogChannels.LC_AMS,
261: "Storage Failure: Can not store Payment ID");
262: }
263: } catch (IOException e) {
264: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
265: Logging.report(Logging.ERROR, LogChannels.LC_AMS,
266: "getPaymentID threw an IOException: "
267: + e.getMessage());
268: }
269: }
270: } catch (RecordStoreException ex) {
271: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
272: Logging.report(Logging.ERROR, LogChannels.LC_AMS,
273: "Storage Failure: Can not read Payment ID");
274: }
275: }
276: return paymentID;
277: }
278:
279: /**
280: * Remove missed transaction for given suite.
281: *
282: * @param suiteId suite ID
283: */
284: public final void removeMissed(int suiteId) {
285: int id = getPaymentID(suiteId);
286: CldcTransactionStoreImpl transactionStore = (CldcTransactionStoreImpl) getTransactionStore();
287: try {
288: transactionStore.removeMissedTransaction(id);
289: } catch (IOException e) {
290: // skip
291: }
292: }
293:
294: /**
295: * The cleanUp method for the TCK tests. Should be removed when not
296: * needed!!! Erases the transaction store.
297: *
298: * @throws IOException indicating Transaction Store failure
299: */
300: public final void cleanUp() throws IOException {
301: getTransactionStore().cleanUp();
302: }
303:
304: // === DEBUG MODE ===
305: /**
306: * Handles the success/failure/random debug mode for the given transaction.
307: * It's called from the parts of the <code>PaymentModule</code> code where
308: * this mode can be applied. If the debug mode is in effect the transaction
309: * state is set accordingly and the method returns <code>true</code>.
310: *
311: * @param transaction the transaction
312: * @return <code>true</code> if the transaction is handled in the method
313: */
314: protected final boolean handleTransactionDebugMode(
315: Transaction transaction) {
316: PaymentInfo paymentInfo = getPaymentInfo(transaction);
317:
318: // (random.nextInt(128) < 64) = approx. one in two will fail
319: if (paymentInfo.isDemoMode()) {
320: if (paymentInfo.getDbgFailIO()
321: || (paymentInfo.getDbgRandomTests() && (random
322: .nextInt(128) < 64))) {
323: transaction.setState(Transaction.FAILED);
324: } else {
325: transaction.setState(Transaction.SUCCESSFUL);
326: }
327:
328: transaction.setNeedsUI(false);
329: return true;
330: }
331:
332: return false;
333: }
334:
335: /**
336: * Handles the auto request debug mode for the given transaction. It's
337: * called from the parts of the <code>PaymentModule</code> code where this
338: * mode can be applied. If the auto request mode is in effect the
339: * transaction state is set accordingly and the method returns
340: * <code>true</code>.
341: *
342: * @param transaction the transaction
343: * @return <code>true</code> if the transaction is handled in the method
344: * (= the auto request mode is in effect)
345: */
346: protected final boolean handleAutoRequestMode(
347: Transaction transaction) {
348: PaymentInfo paymentInfo = getPaymentInfo(transaction);
349:
350: if (!paymentInfo.isDemoMode()) {
351: return false;
352: }
353:
354: switch (paymentInfo.getDbgAutoRequestMode()) {
355: case PaymentInfo.AUTO_REQUEST_REJECT:
356: transaction.setState(Transaction.REJECTED);
357: transaction.setNeedsUI(false);
358: break;
359:
360: case PaymentInfo.AUTO_REQUEST_ACCEPT:
361: int[] providers = getValidProviders(paymentInfo);
362:
363: // we do have at least one supported payment provider
364: assignTransaction(transaction, providers[0]);
365: break;
366:
367: default:
368: return false;
369: }
370:
371: return true;
372: }
373:
374: // === DEBUG MODE ===
375: /** Instance of TransactionStore */
376: private TransactionStore transactionStore;
377:
378: /**
379: * Init and return instance of <code>TransactionStore</code>.
380: *
381: * @return TransactionStore
382: */
383: public TransactionStore getTransactionStore() {
384: if (transactionStore == null) {
385: try {
386: transactionStore = new CldcTransactionStoreImpl(
387: classSecurityToken);
388: } catch (IOException e) {
389: }
390: }
391:
392: return transactionStore;
393: }
394:
395: /** Instance of Utils class */
396: private Utils utilities = getUtilities();
397:
398: /**
399: * Returns an instance of <code>CldcUtils</code> class.
400: *
401: * @return the instance
402: */
403: protected Utils getUtilities() {
404: if (utilities == null) {
405: utilities = new CldcUtils();
406: }
407:
408: return utilities;
409: }
410:
411: /** an preempt token object to pass to donePreempting */
412: private Object PreemptToken;
413:
414: /**
415: * Replaces the current <code>Displayable</code> with the new one if the
416: * <code>nextDisplayable</code> is not <code>null</code> or it recovers
417: * the previous <code>Displayable</code> if the <code>nextDisplayable</code>
418: * is <code>null</code>.
419: *
420: * @param token a security token, which allows preempting
421: * @param nextDisplayable the <code>Displayable</code> to show or
422: * <code>null</code> if the recovery of the old
423: * <code>Displayable</code> is requested
424: */
425: protected void preemptDisplay(SecurityToken token,
426: Displayable nextDisplayable) {
427: DisplayEventHandler d = DisplayEventHandlerFactory
428: .getDisplayEventHandler(token);
429: if (nextDisplayable != null) {
430: try {
431: PreemptToken = d.preemptDisplay(nextDisplayable, true);
432: } catch (InterruptedException ex) {
433: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
434: Logging.report(Logging.ERROR, LogChannels.LC_NONE,
435: "preemptDisplay threw an InterruptedException: "
436: + ex.getMessage());
437: }
438: }
439: } else {
440: d.donePreempting(PreemptToken);
441: PreemptToken = null;
442: }
443: }
444:
445: static {
446: /* Hand out security token */
447: PPSMSAdapter.initSecurityToken(classSecurityToken);
448: }
449: }
|