FileDocCategorySizeDatePackage
CldcTransactionModuleImpl.javaAPI DocphoneME MR2 API (J2ME)12487Wed May 02 18:00:44 BST 2007com.sun.j2me.payment

CldcTransactionModuleImpl.java

/*
 *   
 *
 * 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 javax.microedition.midlet.MIDlet;
import com.sun.midp.midlet.*;
import com.sun.midp.midletsuite.*;

import java.io.IOException;
import java.util.Random;

import javax.microedition.payment.TransactionListener;
import javax.microedition.payment.TransactionRecord;
import javax.microedition.payment.TransactionModuleException;
import javax.microedition.payment.TransactionFeatureException;
import javax.microedition.payment.TransactionListenerException;
import javax.microedition.payment.TransactionPayloadException;

import com.sun.midp.security.Permissions;
import com.sun.midp.util.Properties;
import com.sun.midp.midletsuite.MIDletSuiteStorage;

import java.io.InputStream;
import javax.microedition.rms.RecordStoreException;
import com.sun.midp.log.Logging;
import java.io.ByteArrayInputStream;
import javax.microedition.rms.RecordStoreNotFoundException;
import javax.microedition.rms.RecordStore;
import com.sun.midp.installer.JadProperties;
import com.sun.midp.log.LogChannels;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;

/**
 * This class implements parts of <code>TransactionModuleImpl</code> which
 * are dependent on the CLDC. It's created by a factory method in the payment
 * module.
 *
 * @version 
 * @see CldcPaymentModule#createTransactionModule
 */
public class CldcTransactionModuleImpl extends TransactionModuleImpl {

    /** MDIletSuite being run */
    private MIDletSuite midletSuite;

    /**
     * The name of the file where the Midlet Payment Update Info is stored.
     */
    private static final String PAYMENT_UPDATE_FILE_NAME =
        "payment_update";

    /**
     * MIDlet property for the Payment Version.
     */
    public static final String PAY_VERSION_PROP = "Pay-Version";

    /** MIDletSuite payment info */
    private PaymentInfo paymentInfo = null;

    /**
     * Creates a new instance of <code>CldcTransactionModuleImpl</code>.
     * Requires a reference to the application MIDlet which initiated the
     * payment.
     * <p>
     * Handles the <code>Pay-Debug-FailInitialize</code> debug mode.
     *
     * @param object the caller MIDlet
     * @throws TransactionModuleException indicates an error preventing the
     *      MIDlet from using the payment API
     * @see javax.microedition.payment.TransactionModule#TransactionModule
     */
    protected CldcTransactionModuleImpl(Object object) throws
        TransactionModuleException {
        super(object);

        PaymentInfo paymentInfo = getPaymentInfo();
        if (paymentInfo == null) {
            throw new TransactionModuleException("Missing provisioning " +
                    "information");
        }

        /* Check if MIDletSuite is Trusted */
        // Removed - SecurityException is thrown if untrusted MIDlet tries to use
        // restricted permission, so no additional check is required
//         if (!paymentInfo.isDemoMode() && !getMIDletSuite().isTrusted()) {
//             throw new TransactionModuleException("MidletSuite is untrusted");
//         }

        // === DEBUG MODE ===
        // (PaymentModule.random.nextInt(192) < 64) = approx. one in three will
        // fail in this way
        if (paymentInfo.isDemoMode() && (
            paymentInfo.getDbgFailInitialize() ||
            paymentInfo.getDbgRandomTests() &&
            (CldcPaymentModule.random.nextInt(192) < 64))) {
            throw new TransactionModuleException("Debug mode");
        }
        // === DEBUG MODE ===
    }

    /**
     * Returns the MIDlet suite of the MIDlet.
     *
     * @return the MIDlet suite
     */
    private MIDletSuite getMIDletSuite() {
        if (midletSuite == null) {
            midletSuite =
                (MIDletSuite) Scheduler.getScheduler().getMIDletSuite();
        }
        return midletSuite;
    }

    /**
     * Ensures that the MIDlet will have required privileges to do a protected
     * operation.
     *
     * @param permission the required permission
     * @param name an additional info string
     * @throws SecurityException if the permission is not granted
     * @throws InterruptedException if the thread waiting for the permission
     *      is interrupted
     */
    protected void checkForPermission(int permission, String name) throws
        InterruptedException {
        getMIDletSuite().checkForPermission(permission, name);
    }

    /**
     * Helper class that allows PaymentInfo to be created from MIDletSuiet
     * properies
     */
    class PropertiesWraper extends Properties {
        /** Provision info source */
        private MIDletSuite midletSuite;

        /**
         *  PropertiesWraper constructor
         *
         * @param suite property storage
         */
        public PropertiesWraper(MIDletSuite suite) {
            midletSuite = suite;
        }

        /**
         * Returns property from given MIDletSuite
         *
         * @param key property name
         * @return property value or null
         */
        public String getProperty(String key) {
            return midletSuite.getProperty(key);
        }
    }


    /**
     * Returns the payment provisioning information associated with the MIDlet.
     *
     * @return the payment provisioning information
     */
    protected PaymentInfo getPaymentInfo() {
        if (paymentInfo == null) {
            if (getMIDletSuite().getProperty(PAY_VERSION_PROP) == null) {
                // quick check
                return null;
            }
            RecordStore store;
            PropertiesWraper props = new PropertiesWraper(getMIDletSuite());
            // try to read updated provision info
            try {
                store = RecordStore.openRecordStore(PAYMENT_UPDATE_FILE_NAME,
                    false);
                try {
                    byte[] data = new byte[store.getRecordSize(1)];
                    data = store.getRecord(1);

                    InputStream bis = new ByteArrayInputStream(data);
                    JadProperties payProps = new JadProperties();
                    payProps.load(bis);
                    bis.close();

                    paymentInfo = PaymentInfo.createFromProperties(
                        props, payProps);
                } finally {
                    store.closeRecordStore();
                }
            } catch (RecordStoreNotFoundException ex) {

                // if the is no update file try to read payment info from
                // suite properties
                try {
                    paymentInfo = PaymentInfo.createFromProperties(
                        props, props);
                } catch (PaymentException e) {
                    if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                        Logging.report(Logging.ERROR, LogChannels.LC_AMS,
                                  "getPaymentInfo threw an PaymentException: " +
                                   e.getMessage());
                    }
                }
            } catch (RecordStoreException ex) {
                if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                    Logging.report(Logging.ERROR, LogChannels.LC_AMS,
                              "getPaymentInfo threw an RecordStoreException: " +
                              ex.getMessage());
                }
            } catch (PaymentException ex) {
                if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                    Logging.report(Logging.ERROR, LogChannels.LC_AMS,
                                 "getPaymentInfo threw an PaymentException: " +
                                 ex.getMessage());
                }
            } catch (IOException ex) {
                if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                    Logging.report(Logging.ERROR, LogChannels.LC_AMS,
                                   "getPaymentInfo threw an IOException: " +
                                   ex.getMessage());
                }
            }
            if (paymentInfo != null) {
                // initialize the transaction store for this MIDletSuite only
                // for once
                ((CldcPaymentModule) PaymentModule.getInstance())
                    .initializeTransactionStore(paymentInfo,
                                                midletSuite.getID(),
                    midletSuite.getProperty(MIDletSuiteImpl.SUITE_NAME_PROP));
            }
        }
        return paymentInfo;
    }

    /**
     * Stores the payment provisioning information associated with the MIDlet.
     *
     * @throws IOException indicates an output error
     */
    protected void savePaymentInfo() throws IOException {
        PaymentInfo pInfo = getPaymentInfo();

        if (pInfo != null) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            OutputStreamWriter os = new OutputStreamWriter(bos, "UTF-8");
            byte[] data;

            try {
                pInfo.export(os);
                data = bos.toByteArray();
            } finally {
                os.close();
            }

            RecordStore store = null;
            try {
                store = RecordStore.openRecordStore(PAYMENT_UPDATE_FILE_NAME,
                    true);
                try {
                    if (store.getNumRecords() == 0) {
                        /* Record Store is EMPTY - add new record */
                        store.addRecord(data, 0, data.length);
                    } else {
                        /* Replace first record */
                        store.setRecord(1, data, 0, data.length);
                    }
                } finally {
                    store.closeRecordStore();
                }
            } catch (RecordStoreException e) {
                throw new IOException(
                    "Storage Failure: Can not store Payment Info");
            }
        }

    }

    /**
     * Returns the MIDlet payment ID that can be used to store transaction
     * records for the MIDlet initiated transactions into the transaction store.
     *
     * @return the ID
     */
    protected int getApplicationID() {
        return ((CldcPaymentModule) PaymentModule.getInstance())
            .getPaymentID(getMIDletSuite().getID());
    }

    /**
     * Returns an array of the missed transactions associated with the MIDlet
     * suite.
     * <p>
     * It handles the <code>Pay-Debug-MissedTransactions</code> debug mode.
     *
     * @return an array of the missed transactions
     */
    protected TransactionRecord[] getMissedTransactions() {
        // === DEBUG MODE ===
        PaymentInfo paymentInfo = getPaymentInfo();
        if (paymentInfo.isDemoMode() &&
            paymentInfo.getDbgMissedTransactions() >= 0) {
            int applicationID = getApplicationID();

            CldcPaymentModule paymentModule = (CldcPaymentModule)
                                              PaymentModule.getInstance();
            CldcTransactionStoreImpl transactionStore =
                (CldcTransactionStoreImpl) paymentModule.getTransactionStore();
            TransactionRecord[] fakeMissed = null;

            try {
                fakeMissed = transactionStore.getMissedTransactions(
                    applicationID, true);
            } catch (IOException e) {
            }

            return fakeMissed;
        }
        // === DEBUG MODE ===
        return super.getMissedTransactions();
    }
}