FileDocCategorySizeDatePackage
AutoTesterBase.javaAPI DocphoneME MR2 API (J2ME)20487Wed May 02 18:00:02 BST 2007com.sun.midp.installer

AutoTesterBase.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.midp.installer;

import java.io.IOException;

import javax.microedition.io.ConnectionNotFoundException;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;

import com.sun.midp.i18n.Resource;
import com.sun.midp.i18n.ResourceConstants;

import com.sun.midp.midlet.MIDletSuite;

import com.sun.midp.midletsuite.MIDletInfo;
import com.sun.midp.midletsuite.MIDletSuiteStorage;

import com.sun.midp.security.Permissions;

import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels;

/**
 * Installs/Updates a test suite, runs the first MIDlet in the suite in a loop
 * until the new version of the suite is not found, then removes the suite.
 * <p>
 * The MIDlet uses these application properties as arguments: </p>
 * <ol>
 *   <li>arg-0: URL for the test suite
 *   <li>arg-1: Used to override the default domain used when installing
 *    an unsigned suite. The default is maximum to allow the runtime API tests
 *    be performed automatically without tester interaction. The domain name
 *    may be followed by a colon and a list of permissions that must be allowed
 *    even if they are not listed in the MIDlet-Permissions attribute in the
 *    application descriptor file. Instead of the list a keyword "all" can be
 *    specified indicating that all permissions must be allowed, for example:
 *    operator:all.
 * </ol>
 * <p>
 * If arg-0 is not given then a form will be used to query the tester for
 * the arguments.</p>
 */
class AutoTesterBase extends MIDlet implements CommandListener,
    Runnable {

    /** Standard timeout for alerts. */
    static final int ALERT_TIMEOUT = 5000;
    /** Contains the default URL. */
    static final String defaultUrl = "http://";

    /** Display for this MIDlet. */
    Display display;
    /** Parameter form if there is not URL parameter given. */
    Form parameterForm;
    /** Contains the URL the user typed in. */
    TextField urlTextField;
    /** Contains the domain the user typed in. */
    TextField domainTextField;
    /** Command object for "Exit" command in the URL screen. */
    Command endCmd = new Command(Resource.getString
                                         (ResourceConstants.EXIT),
                                         Command.EXIT, 1);
    /** Command object for URL screen start testing. */
    Command testCmd =
        new Command(Resource.getString(ResourceConstants.GO),
                    Command.SCREEN, 1);
    /** URL of the test suite. */
    String url;
    /** Security domain to assign to unsigned suites. */
    String domain = Permissions.UNIDENTIFIED_DOMAIN_BINDING;
    /** MIDlet suite storage object. */
    MIDletSuiteStorage midletSuiteStorage;
    /** The installer. */
    Installer installer;
    /** How many iterations to run the suite */
    int loopCount = -1;

    /** The InstallListener to use when creating the Installer. */
    protected InstallListener installListener;

    /**
     * Create and initialize a new auto tester MIDlet.
     */
    AutoTesterBase() {
        display = Display.getDisplay(this);

        // The arg-<n> properties are generic command arguments
        url = getAppProperty("arg-0");
        if (url != null) {
            // URL given as a argument, look for a domain arg and then start
            String arg1 = getAppProperty("arg-1");

            boolean hasLoopCount = false;
            if (arg1 != null) {
                // this can be domain or loop count
                try {
                    loopCount = Integer.parseInt(arg1);
                    hasLoopCount = true;
                } catch (NumberFormatException e) {
                    // then its domain
                    domain = arg1;
                }

                if (!hasLoopCount) {
                    String arg2 = getAppProperty("arg-2");
                    if (arg2 != null) {
                        try {
                            loopCount = Integer.parseInt(arg2);
                        } catch (NumberFormatException e) {
                            // just ignore
                        }
                    }
                }
            }
        }
    }

    /**
     * Start.
     */
    public void startApp() {
        // Avoid competing for foreground with Test MIDlet
        display.setCurrent(null);
        notifyPaused();
    }

    /**
     * Pause; there are no resources that need to be released.
     */
    public void pauseApp() {
    }

    /**
     * Destroy cleans up.
     *
     * @param unconditional is ignored; this object always
     * destroys itself when requested.
     */
    public void destroyApp(boolean unconditional) {
    }

    /**
     * Respond to a command issued on any Screen.
     *
     * @param c command activated by the user
     * @param s the Displayable the command was on.
     */
    public void commandAction(Command c, Displayable s) {
        if (c == testCmd) {
            getURLTextAndTest();
        } else if (c == endCmd || c == Alert.DISMISS_COMMAND) {
            // goto back to the manager midlet
            notifyDestroyed();
        }
    }

    /**
     * Ask the user for the URL.
     */
    void getUrl() {
        try {
            parameterForm = new
                Form(Resource.getString
                     (ResourceConstants.AMS_AUTO_TESTER_TESTSUITE_PARAM));

            urlTextField = new TextField
                (Resource.getString(ResourceConstants.AMS_AUTO_TESTER_URL),
                              defaultUrl, 1024, TextField.ANY);
            urlTextField.setLayout(Item.LAYOUT_NEWLINE_AFTER | Item.LAYOUT_2);
            parameterForm.append(urlTextField);

            domainTextField =
                new TextField(Resource.getString(ResourceConstants.
                              AMS_AUTO_TESTER_UNSIGN_SECURITY_DOMAIN),
                              domain, 1024, TextField.ANY);
            domainTextField.setLayout(Item.LAYOUT_NEWLINE_AFTER |
                                      Item.LAYOUT_2);
            parameterForm.append(domainTextField);

            parameterForm.addCommand(endCmd);
            parameterForm.addCommand(testCmd);
            parameterForm.setCommandListener(this);

            display.setCurrent(parameterForm);
        } catch (Exception ex) {
            displayException(Resource.getString(ResourceConstants.EXCEPTION),
                             ex.toString());
        }
    }

    /**
     * Save the URL setting the user entered in to the urlTextBox.
     */
    void getURLTextAndTest() {
        url = urlTextField.getString();

        if (url == null || url.length() == 0) {
            Alert a = new Alert(Resource.getString(ResourceConstants.ERROR),
                                Resource.getString(ResourceConstants.
                                       AMS_AUTO_TESTER_ERROR_URL_MSG),
                                   null, AlertType.ERROR);
            a.setTimeout(ALERT_TIMEOUT);
            display.setCurrent(a, parameterForm);
            return;
        }

        domain = domainTextField.getString();

        if (domain == null || domain.length() == 0) {
            Alert a = new Alert(Resource.getString(ResourceConstants.ERROR),
                                Resource.getString(ResourceConstants.
                                    AMS_AUTO_TESTER_ERROR_SECURITY_DOMAIN_MSG),
                                null, AlertType.ERROR);
            a.setTimeout(ALERT_TIMEOUT);
            display.setCurrent(a, parameterForm);
            return;
        }

        startBackgroundTester();
    }

    /**
     * Start the background tester.
     */
    void startBackgroundTester() {
        midletSuiteStorage = MIDletSuiteStorage.getMIDletSuiteStorage();

        installer = new HttpInstaller();
        if (domain != null) {
            String additionalPermissions = null;
            int index = domain.indexOf(":");
            int len = domain.length();

            if (index > 0 && index + 1 < len) {
                additionalPermissions = domain.substring(index + 1, len);
                domain = domain.substring(0, index);
            }

            installer.setUnsignedSecurityDomain(domain);
            installer.setExtraPermissions(additionalPermissions);
        }

        new Thread(this).start();
    }

    /** Run the installer. */
    public void run() {
    }

    /**
     * Handles an installer exceptions.
     *
     * @param suiteId ID of the suite being installed, can be null
     * @param ex exception to handle
     */
    void handleInstallerException(int suiteId, Throwable ex) {
        String message = null;

        if (ex instanceof InvalidJadException) {
            InvalidJadException ije = (InvalidJadException)ex;

            /*
             * The server will signal the end of testing with not found
             * status. However print out the JAD not found error if this
             * is the first download. (suiteId == null)
             */
            int reason = ije.getReason();
            if ((reason != InvalidJadException.JAD_NOT_FOUND &&
                reason != InvalidJadException.JAD_SERVER_NOT_FOUND) ||
                    suiteId == MIDletSuite.UNUSED_SUITE_ID) {
                message = "** Error installing suite (" + reason + "): " +
                          messageForInvalidJadException(ije);
            }
        } else if (ex instanceof IOException) {
            message = "** I/O Error installing suite: " + ex.getMessage();
        } else {
            message = "** Error installing suite: " + ex.toString();
        }

        if (message != null) {
            displayException(Resource.getString(ResourceConstants.ERROR),
                             message);

            long start = System.currentTimeMillis();
            long time_left = ALERT_TIMEOUT;

            while (time_left > 0) {
                try {
                    Thread.sleep(time_left);
                    time_left = 0;
                } catch (InterruptedException ie) {
                    long tmp = System.currentTimeMillis();
                    time_left -= (tmp - start);
                    start = tmp;
                }
            }
        }
    }

    /**
     * Display an exception to the user, with a done command.
     *
     * @param title exception form's title
     * @param message exception message
     */
    void displayException(String title, String message) {
        Alert a = new Alert(title, message, null, AlertType.ERROR);

        // This application must log always.
        Logging.report(Logging.CRITICAL, LogChannels.LC_CORE, message);
        a.setTimeout(ALERT_TIMEOUT);
        a.setCommandListener(this);

        display.setCurrent(a);
    }

    /**
     * Returns the class name of the first MIDlet of the newly installed
     * suite.
     *
     * @param suiteId ID of the MIDlet Suite
     * @param midletSuiteStorage MIDlet suite storage to look up properties
     *
     * @return an object with the class name and display name of
     * the suite's MIDlet-1 property
     */
    static MIDletInfo getFirstMIDletOfSuite(int suiteId,
            MIDletSuiteStorage midletSuiteStorage) {
        MIDletSuite ms = null;
        String name = null;

        try {
            ms = midletSuiteStorage.getMIDletSuite(suiteId, false);
            name = ms.getProperty("MIDlet-1");
        } catch (Exception e) {
            throw new RuntimeException("midlet properties corrupted");
        } finally {
            if (ms != null) {
                ms.close();
            }
        }

        if (name == null) {
            throw new RuntimeException("MIDlet-1 missing");
        }

        // found the entry now parse out the class name, and display name
        return new MIDletInfo(name);
    }

    /**
     * Returns the associated message for the given exception.
     * This function is here instead of in the exception its self because
     * it not need on devices, it needed only on development platforms that
     * have command line interface.
     *
     * @param ije reason reason code for the exception
     *
     * @return associated message for the given reason
     */
    static String messageForInvalidJadException(InvalidJadException ije) {
        switch (ije.getReason()) {
        case InvalidJadException.MISSING_PROVIDER_CERT:
        case InvalidJadException.MISSING_SUITE_NAME:
        case InvalidJadException.MISSING_VENDOR:
        case InvalidJadException.MISSING_VERSION:
        case InvalidJadException.MISSING_JAR_URL:
        case InvalidJadException.MISSING_JAR_SIZE:
        case InvalidJadException.MISSING_CONFIGURATION:
        case InvalidJadException.MISSING_PROFILE:
            return "A required attribute is missing";

        case InvalidJadException.SUITE_NAME_MISMATCH:
        case InvalidJadException.VERSION_MISMATCH:
        case InvalidJadException.VENDOR_MISMATCH:
            return "A required suite ID attribute in the JAR manifest " +
                "do not match the one in the JAD";

        case InvalidJadException.ATTRIBUTE_MISMATCH:
            return "The value for " + ije.getExtraData() + " in the " +
                "trusted JAR manifest did not match the one in the JAD";

        case InvalidJadException.CORRUPT_PROVIDER_CERT:
            return "The content provider certificate cannot be decoded.";

        case InvalidJadException.UNKNOWN_CA:
            return "The content provider certificate issuer " +
                ije.getExtraData() + " is unknown.";

        case InvalidJadException.INVALID_PROVIDER_CERT:
            return "The signature of the content provider certificate " +
                "is invalid.";

        case InvalidJadException.CORRUPT_SIGNATURE:
            return "The JAR signature cannot be decoded.";

        case InvalidJadException.INVALID_SIGNATURE:
            return "The signature of the JAR is invalid.";

        case InvalidJadException.UNSUPPORTED_CERT:
            return "The content provider certificate is not a supported " +
                "version.";

        case InvalidJadException.EXPIRED_PROVIDER_CERT:
            return "The content provider certificate is expired.";

        case InvalidJadException.EXPIRED_CA_KEY:
            return "The public key of " + ije.getExtraData() +
                " has expired.";

        case InvalidJadException.JAR_SIZE_MISMATCH:
            return "The Jar downloaded was not the size in the JAD";

        case InvalidJadException.OLD_VERSION:
            return "The application is an older version of one that is " +
                "already installed";

        case InvalidJadException.NEW_VERSION:
            return "The application is an newer version of one that is " +
                "already installed";

        case InvalidJadException.INVALID_JAD_URL:
            return "The JAD URL is invalid";

        case InvalidJadException.JAD_SERVER_NOT_FOUND:
            return "JAD server not found";

        case InvalidJadException.JAD_NOT_FOUND:
            return "JAD not found";

        case InvalidJadException.INVALID_JAR_URL:
            return "The JAR URL in the JAD is invalid: " +
                ije.getExtraData();

        case InvalidJadException.JAR_SERVER_NOT_FOUND:
            return "JAR server not found: " + ije.getExtraData();

        case InvalidJadException.JAR_NOT_FOUND:
            return "JAR not found: " + ije.getExtraData();

        case InvalidJadException.CORRUPT_JAR:
            return "Corrupt JAR, error while reading: " +
                ije.getExtraData();

        case InvalidJadException.INVALID_JAR_TYPE:
            if (ije.getExtraData() != null) {
                return "JAR did not have the correct media type, it had " +
                    ije.getExtraData();
            }

            return "The server did not have a resource with an " +
                "acceptable media type for the JAR URL. (code 406)";

        case InvalidJadException.INVALID_JAD_TYPE:
            if (ije.getExtraData() != null) {
                String temp = ije.getExtraData();

                if (temp.length() == 0) {
                    return "JAD did not have a media type";
                }

                return "JAD did not have the correct media type, it had " +
                    temp;
            }

            /*
             * Should not happen, the accept field is not send
             * when getting the JAD.
             */
            return "The server did not have a resource with an " +
                "acceptable media type for the JAD URL. (code 406)";

        case InvalidJadException.INVALID_KEY:
            return "The attribute key [" + ije.getExtraData() +
                "] is not in the proper format";

        case InvalidJadException.INVALID_VALUE:
            return "The value for attribute " + ije.getExtraData() +
                " is not in the proper format";

        case InvalidJadException.INSUFFICIENT_STORAGE:
            return "There is insufficient storage to install this suite";

        case InvalidJadException.UNAUTHORIZED:
            return "Authentication required or failed";

        case InvalidJadException.JAD_MOVED:
            return "The JAD to be installed is for an existing suite, " +
                "but not from the same domain as the existing one: " +
                ije.getExtraData();

        case InvalidJadException.CANNOT_AUTH:
            return
                "Cannot authenticate with the server, unsupported scheme";

        case InvalidJadException.DEVICE_INCOMPATIBLE:
            return "Either the configuration or profile is not supported.";

        case InvalidJadException.ALREADY_INSTALLED:
            return
                "The JAD matches a version of a suite already installed.";

        case InvalidJadException.AUTHORIZATION_FAILURE:
            return "The suite is not authorized for " + ije.getExtraData();

        case InvalidJadException.PUSH_DUP_FAILURE:
            return "The suite is in conflict with another application " +
                "listening for network data on " + ije.getExtraData();

        case InvalidJadException.PUSH_FORMAT_FAILURE:
            return "Push attribute in incorrectly formated: " +
                ije.getExtraData();

        case InvalidJadException.PUSH_PROTO_FAILURE:
            return "Connection in push attribute is not supported: " +
                ije.getExtraData();

        case InvalidJadException.PUSH_CLASS_FAILURE:
            return "The class in push attribute not in a MIDlet-<n> " +
                "attribute: " + ije.getExtraData();

        case InvalidJadException.TRUSTED_OVERWRITE_FAILURE:
            return "Cannot update a trusted suite with an untrusted " +
                "version";

        case InvalidJadException.INVALID_CONTENT_HANDLER:
	    return "Content handler attribute(s) incorrectly formatted: " +
		ije.getExtraData();

	case InvalidJadException.CONTENT_HANDLER_CONFLICT:
	    return "Content handler would conflict with another handler: " +
		ije.getExtraData();

        case InvalidJadException.CA_DISABLED:
            return "The application can't be authorized because " +
                ije.getExtraData() + " is disabled.";

        case InvalidJadException.UNSUPPORTED_CHAR_ENCODING:
            return "Unsupported character encoding: " + ije.getExtraData();
        }

        return ije.getMessage();
    }
}