0001: /*
0002: *
0003: *
0004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: */
0026:
0027: package com.sun.midp.content;
0028:
0029: import com.sun.midp.io.HttpUrl;
0030:
0031: import com.sun.midp.security.SecurityInitializer;
0032: import com.sun.midp.security.SecurityToken;
0033: import com.sun.midp.security.ImplicitlyTrustedClass;
0034:
0035: import com.sun.midp.midlet.MIDletSuite;
0036:
0037: import java.io.IOException;
0038:
0039: import java.util.Hashtable;
0040: import java.util.Vector;
0041:
0042: import javax.microedition.content.*;
0043: import javax.microedition.io.Connector;
0044:
0045: /**
0046: * Implementation of Content Handler registry. It maintains
0047: * the set of currently registered handlers and updates to
0048: * the file that holds the permanent set.
0049: * The RegistryImpl class maintains an array of the current
0050: * registrations that is initialized on first use.
0051: */
0052: public final class RegistryImpl {
0053:
0054: /**
0055: * Inner class to request security token from SecurityInitializer.
0056: * SecurityInitializer should be able to check this inner class name.
0057: */
0058: static private class SecurityTrusted implements
0059: ImplicitlyTrustedClass {
0060: };
0061:
0062: /** This class has a different security domain than the MIDlet suite */
0063: private static SecurityToken classSecurityToken = SecurityInitializer
0064: .requestToken(new SecurityTrusted());
0065:
0066: static {
0067: AppProxy.setSecurityToken(classSecurityToken);
0068: RegistryStore.setSecurityToken(classSecurityToken);
0069: }
0070:
0071: /** The set of active Invocations. */
0072: private final Hashtable activeInvocations = new Hashtable();
0073:
0074: /** The set of active RegistryImpls. */
0075: private static Hashtable registries = new Hashtable();
0076:
0077: /** The mutex used to avoid corruption between threads. */
0078: private static final Object mutex = new Object();
0079:
0080: /** Implementation of the listener. */
0081: private ResponseListenerImpl listenerImpl;
0082:
0083: /** The ContentHandlerImpl that matches the classname of this Registry. */
0084: private ContentHandlerImpl handlerImpl;
0085:
0086: /** The Registry that is delegating to this RegistryImpl. */
0087: private Registry registry;
0088:
0089: /** The AppProxy for this registry. */
0090: final AppProxy application;
0091:
0092: /** Count of responses received. */
0093: int responseCalls;
0094:
0095: /**
0096: * Gets the RegistryImpl for the application class.
0097: * The SecurityToken is needed to call from the public API package.
0098: * The application is identified by the classname that implements
0099: * the lifecycle of the Java runtime environment.
0100: * The classname must be the name of a registered application class
0101: * or a registered content handler.
0102: * <p>
0103: * For a MIDP implementation,
0104: * application classes must be registered with the
0105: * <code>MIDlet-<n></code> attribute; content handlers are
0106: * registered with the <code>MicroEdition-Handler-<n></code>
0107: * attribute or the {@link #register register} method.
0108: * <p>
0109: * When the RegistryImpl is created (the first time) all of the
0110: * existing Invocations are marked. They will be subject to
0111: * {@link #cleanup} when the MIDlet exits.
0112: *
0113: * @param classname the application class
0114: * @param token the security token needed to control access to the impl
0115: *
0116: * @return a RegistryImpl instance providing access to content handler
0117: * registrations and invocations; MUST NOT be <code>null</code>
0118: * @exception ContentHandlerException is thrown with a reason of
0119: * <code>NO_REGISTERED_HANDLER</code> if there is no
0120: * content handler registered for the classname in the current
0121: * application
0122: * @exception NullPointerException if <code>classname</code> is
0123: * <code>null</code>
0124: */
0125: public static RegistryImpl getRegistryImpl(String classname,
0126: Object token) throws ContentHandlerException {
0127: AppProxy.checkAPIPermission(token);
0128: return getRegistryImpl(classname);
0129: }
0130:
0131: /**
0132: * Gets the RegistryImpl for the application class.
0133: * The application is identified by the classname that implements
0134: * the lifecycle of the Java runtime environment.
0135: * The classname must be the name of a registered application class
0136: * or a registered content handler.
0137: * <p>
0138: * For a MIDP implementation,
0139: * application classes must be registered with the
0140: * <code>MIDlet-<n></code> attribute; content handlers are
0141: * registered with the <code>MicroEdition-Handler-<n></code>
0142: * attribute or the {@link #register register} method.
0143: * <p>
0144: * When the RegistryImpl is created (the first time) all of the
0145: * existing Invocations are marked. They will be subject to
0146: * {@link #cleanup} when the MIDlet exits.
0147: *
0148: * @param classname the application class
0149: *
0150: * @return a RegistryImpl instance providing access to content handler
0151: * registrations and invocations; MUST NOT be <code>null</code>
0152: * @exception ContentHandlerException is thrown with a reason of
0153: * <code>NO_REGISTERED_HANDLER</code> if there is no
0154: * content handler registered for the classname in the current
0155: * application
0156: * @exception NullPointerException if <code>classname</code> is
0157: * <code>null</code>
0158: */
0159: static RegistryImpl getRegistryImpl(String classname)
0160: throws ContentHandlerException {
0161: // Synchronize between competing operations
0162: RegistryImpl curr = null;
0163: synchronized (mutex) {
0164: // Check if class already has a RegistryImpl
0165: curr = (RegistryImpl) registries.get(classname);
0166: if (curr != null) {
0167: // Check that it is still a CH or MIDlet
0168: if (curr.handlerImpl == null
0169: && (!curr.application.isRegistered())) {
0170: // Classname is not a registered MIDlet or ContentHandler
0171: throw new ContentHandlerException(
0172: "not a registered MIDlet",
0173: ContentHandlerException.NO_REGISTERED_HANDLER);
0174: }
0175: return curr;
0176: }
0177:
0178: // Create a new instance and insert it into the list
0179: curr = new RegistryImpl(classname);
0180: registries.put(classname, curr);
0181: }
0182:
0183: /*
0184: * Unsynchronized, a new RegistryImpl has been created.
0185: * Mark any existing Invocations so that at cleanup the pre-existing
0186: * Invocations can be handled properly.
0187: */
0188: InvocationStore.setCleanup(curr.application.getStorageId(),
0189: classname, true);
0190: return curr;
0191: }
0192:
0193: /**
0194: * RegistryImpl constructor and insert the instance in the
0195: * list of registered applications.
0196: *
0197: * @param classname the application class for this instance
0198: *
0199: * @exception ContentHandlerException if
0200: * the <code>classname</code> is not registered either
0201: * as a MIDlet or a content handler
0202: * @exception NullPointerException if <code>classname</code>
0203: * is <code>null</code>
0204: * @exception SecurityException is thrown if the caller
0205: * does not have the correct permision
0206: */
0207: private RegistryImpl(String classname)
0208: throws ContentHandlerException {
0209: try {
0210: // Get the application for the class
0211: application = AppProxy.getCurrent().forClass(classname);
0212: } catch (ClassNotFoundException cnfe) {
0213: throw new ContentHandlerException("not an application",
0214: ContentHandlerException.NO_REGISTERED_HANDLER);
0215: } catch (IllegalArgumentException iae) {
0216: throw new ContentHandlerException("not an application",
0217: ContentHandlerException.NO_REGISTERED_HANDLER);
0218: }
0219:
0220: /* Remember the ContentHandlerImpl, if there is one. */
0221: handlerImpl = getServer(application);
0222:
0223: if (handlerImpl == null && (!application.isRegistered())) {
0224: // Classname is not a registered MIDlet or ContentHandler; fail
0225: throw new ContentHandlerException(
0226: "not a registered MIDlet",
0227: ContentHandlerException.NO_REGISTERED_HANDLER);
0228: }
0229: }
0230:
0231: /**
0232: * Sets the Registry that is delegating to this instance.
0233: * Settable only once.
0234: * Synchronization is performed in
0235: * {@link javax.microedition.content.Registry#register}.
0236: * @param newRegistry the Registry delegating to this
0237: * @see #getRegistry
0238: */
0239: public void setRegistry(Registry newRegistry) {
0240: if (registry == null) {
0241: registry = newRegistry;
0242: }
0243: }
0244:
0245: /**
0246: * Gets the Registry that is delegating to this RegistryImpl.
0247: * @return a Registry instance
0248: * @see #setRegistry
0249: */
0250: public Registry getRegistry() {
0251: return registry;
0252: }
0253:
0254: /**
0255: * Cleanup as necessary for this classname, both for ContentHandlerServer
0256: * and the registry.
0257: * Cleanup is required by the fault handling descriptions in
0258: * {@link javax.microedition.content.ContentHandlerServer}.
0259: * <ul>
0260: * <li>
0261: * If an Invocation with a status of <code>ACTIVE</code> is dequeued by
0262: * the content handler, but the handler does not call
0263: * {@link javax.microedition.content.ContentHandlerServer#finish finish}
0264: * or make a request to chain a new Invocation to the ACTIVE
0265: * invocation before the content handler exits, then the AMS MUST
0266: * complete the request with an ERROR status.
0267: * </li>
0268: * <li>
0269: * If the content handler is not running, or exits before processing
0270: * all queued requests or responses, then it MUST be started.
0271: * The content handler is expected to dequeue at least one
0272: * invocation that was queued before it was started.
0273: * If it does not dequeue any pending Invocations, then Invocations
0274: * that were in the queue for the content handler
0275: * before it was started MUST be handled as follows:
0276: * <ul>
0277: * <li>Invocation requests with a status of <code>ACTIVE</code>
0278: * are completed with the <code>ERROR</code> status.</li>
0279: * <li>Invocation responses are discarded.</li>
0280: * <li>Invocations queued after the content handler was started are
0281: * retained and will require it to be restarted.</li>
0282: * </ul>
0283: * </li>
0284: * </ul>
0285: * @param suiteId the MIDletSuite to cleanup after
0286: * @param classname the application class to cleanup
0287: */
0288: static void cleanup(int suiteId, String classname) {
0289: InvocationImpl invoc = null;
0290: while ((invoc = InvocationStore.getCleanup(suiteId, classname)) != null) {
0291: invoc.setStatus(Invocation.ERROR);
0292: }
0293: }
0294:
0295: /**
0296: * Create and initialize a new ContentHandler server with
0297: * type(s), suffix(es), and action(s), action name(s),
0298: * access restrictions and content handler ID.
0299: * Compute the application name, ID, and version
0300: *
0301: * @param classname the application class name that implements
0302: * this content handler. The value MUST NOT be <code>null</code>
0303: * and MUST implement the lifecycle of the Java runtime
0304: * @param types an array of types to register;
0305: * if <code>null</code> it is treated the same as an empty array
0306: * @param suffixes an array of suffixes to register;
0307: * if <code>null</code> it is treated the same as an empty array
0308: * @param actions an array of actions to register;
0309: * if <code>null</code> it is treated the same as an empty array
0310: * @param actionnames an array of ActionNameMaps to register;
0311: * if <code>null</code> it is treated the same as an empty array
0312: * @param id the content handler ID; if <code>null</code>
0313: * a non-null value MUST be provided by the implementation
0314: * @param accessRestricted the IDs of applications and content
0315: * handlers that are
0316: * allowed visibility and access to this content handler;
0317: * if <code>null</code> then all applications and content
0318: * handlers are allowed access; if <code>non-null</code>, then
0319: * ONLY applications and content handlers with matching IDs are
0320: * allowed access.
0321: * @param appl the AppProxy registering the handler
0322: *
0323: * @return the registered ContentHandler; MUST NOT be <code>null</code>
0324: * @exception NullPointerException if any of the following items is
0325: * <code>null</code>:
0326: * <ul>
0327: * <li>classname</li>
0328: * <li>any types, suffixes, actions, actionnames, or
0329: * accessRestricted array element</li>,
0330: * <li>msuite</li>
0331: * </ul>
0332: *
0333: * @exception IllegalArgumentException can be thrown:
0334: * <ul>
0335: * <li>if any of the <code>types</code>, <code>suffix</code>,
0336: * <code>actions</code>, or <code>accessRestricted</code>
0337: * strings have a length of zero, or </li>
0338: * <li>if the <code>classname</code> does not implement the valid
0339: * lifecycle for the Java Runtime,</li>
0340: * <li>if the sequence of actions in each ActionNameMap
0341: * is not the same as the sequence of <code>actions</code>,
0342: * or </li>
0343: * <li>if the locales of the ActionNameMaps are not unique, or.</li>
0344: * <li>if the length of the <code>accessRestricted</code>
0345: * array is zero.</li>.
0346: * </ul>
0347: */
0348: static ContentHandlerImpl newHandler(String classname,
0349: String[] types, String[] suffixes, String[] actions,
0350: ActionNameMap[] actionnames, String id,
0351: String[] accessRestricted, AppProxy appl)
0352: throws IllegalArgumentException {
0353: // Default the ID if not supplied
0354: if (id == null) {
0355: // Generate a unique ID based on the MIDlet suite
0356: id = appl.getDefaultID();
0357: }
0358:
0359: // Create a new ContentHandler instance
0360: ContentHandlerImpl handler = new ContentHandlerImpl(types,
0361: suffixes, actions, actionnames, id, accessRestricted,
0362: appl.getAuthority());
0363: handler.classname = classname;
0364: handler.storageId = appl.getStorageId();
0365: handler.appname = appl.getApplicationName();
0366: handler.version = appl.getVersion();
0367: return handler;
0368: }
0369:
0370: /**
0371: * Registers the application class using content
0372: * type(s), suffix(es), and action(s), action name(s),
0373: * access restrictions and content handler ID.
0374: * <p>
0375: * An application can use this method to replace or update
0376: * its own registrations
0377: * that have the same classname with new information.
0378: * The update occurs atomically; the update to the registry
0379: * either occurs or it does not.
0380: * <p>
0381: * The content handler may request to the following
0382: * items:
0383: * <ul>
0384: * <li>zero or more content types</li>
0385: * <li>zero or more suffixes</li>
0386: * <li>zero or more actions</li>
0387: * <li>zero or more mappings from actions to action names</li>
0388: * <li>zero or more access restrictions</li>
0389: * <li>a optional application ID</li>
0390: * </ul>
0391: *
0392: * <p>
0393: * If no exceptions are thrown, then the type(s), suffix(s), action(s),
0394: * action names, and access restrictions, and ID
0395: * will be registered for the application class.
0396: * <p>
0397: * If an exception is thrown, then the previous registration, if
0398: * any, will not be removed or modified.
0399: *
0400: * @param classname the application class name that implements
0401: * this content handler. The value MUST NOT be <code>null</code>
0402: * and MUST implement the lifecycle of the Java runtime
0403: * @param types an array of types to register;
0404: * if <code>null</code> it is treated the same as an empty array
0405: * @param suffixes an array of suffixes to register;
0406: * if <code>null</code> it is treated the same as an empty array
0407: * @param actions an array of actions to register;
0408: * if <code>null</code> it is treated the same as an empty array
0409: * @param actionnames an array of ActionNameMaps to register;
0410: * if <code>null</code> it is treated the same as an empty array
0411: * @param id the content handler ID; if <code>null</code>
0412: * a non-null value MUST be provided by the implementation
0413: * @param accessRestricted the IDs of applications and content
0414: * handlers that are
0415: * allowed visibility and access to this content handler;
0416: * if <code>null</code> then all applications and content
0417: * handlers are allowed access; if <code>non-null</code>, then
0418: * ONLY applications and content handlers with matching IDs are
0419: * allowed access.
0420: *
0421: * @return the registered ContentHandler; MUST NOT be <code>null</code>
0422: * @exception NullPointerException if any of the following items is
0423: * <code>null</code>:
0424: * <ul>
0425: * <li>classname</li>
0426: * <li>any types, suffixes, actions, actionnames, or
0427: * accessRestricted array element</li>
0428: * </ul>
0429: *
0430: * @exception IllegalArgumentException can be thrown:
0431: * <ul>
0432: * <li>if any of the <code>types</code>, <code>suffix</code>,
0433: * <code>actions</code>, or <code>accessRestricted</code>
0434: * strings have a length of zero, or </li>
0435: * <li>if the <code>classname</code> does not implement the valid
0436: * lifecycle for the Java Runtime,</li>
0437: * <li>if the sequence of actions in each ActionNameMap
0438: * is not the same as the sequence of <code>actions</code>,
0439: * or </li>
0440: * <li>if the locales of the ActionNameMaps are not unique, or.</li>
0441: * <li>if the length of the <code>accessRestricted</code>
0442: * array is zero.</li>.
0443: * </ul>
0444: * @exception ClassNotFoundException if the <code>classname</code>
0445: * is not present
0446: * @exception ContentHandlerException with a error code of
0447: * {@link ContentHandlerException#AMBIGUOUS} if <code>id</code>
0448: * is a prefix of any registered handler or if any registered
0449: * handler ID is a prefix of this ID
0450: * @exception SecurityException if registration
0451: * is not permitted
0452: */
0453: public ContentHandlerImpl register(String classname,
0454: String[] types, String[] suffixes, String[] actions,
0455: ActionNameMap[] actionnames, String id,
0456: String[] accessRestricted) throws SecurityException,
0457: IllegalArgumentException, ClassNotFoundException,
0458: ContentHandlerException {
0459: application.checkRegisterPermission("register");
0460:
0461: // May throw ClassNotFoundException or IllegalArgumentException
0462: AppProxy appl = application.forClass(classname);
0463:
0464: synchronized (mutex) {
0465: // Create a new ContentHandler instance
0466: ContentHandlerImpl handler = newHandler(classname, types,
0467: suffixes, actions, actionnames, id,
0468: accessRestricted, appl);
0469: handler.registrationMethod = ContentHandlerImpl.REGISTERED_DYNAMIC;
0470:
0471: ContentHandlerImpl conflict = checkConflicts(handler);
0472: if (conflict != null) {
0473: unregister(classname);
0474: }
0475:
0476: RegistryStore.register(handler);
0477: setServer(handler);
0478:
0479: if (AppProxy.LOG_INFO) {
0480: appl.logInfo("Register: " + classname + ", id: "
0481: + handler.getID());
0482: }
0483:
0484: return handler;
0485: }
0486: }
0487:
0488: /**
0489: * Sets the ContentHandlerImpl; update any active RegistryImpl.
0490: * Replaces the entry in RegisteredTypes list as well.
0491: *
0492: * @param server the ContentHandlerImpl for this RegistryImpl
0493: * @see javax.microedition.content.ContentHandlerServerImpl
0494: */
0495: public void setServer(ContentHandlerImpl server) {
0496: synchronized (mutex) {
0497: // Update the RegistryImpl, if any, this is a server for
0498: RegistryImpl impl = (RegistryImpl) registries
0499: .get(server.classname);
0500: if (impl != null) {
0501: impl.handlerImpl = server;
0502: }
0503: }
0504: }
0505:
0506: /**
0507: * Check for conflicts between a proposed new handler and the existing
0508: * handlers. If the handler is being replaced it will be returned.
0509: * Locate and return any existing handler for the same classname.
0510: *
0511: * @param handler the new content handler
0512: *
0513: * @return a ContentHandlerImpl within the suite that
0514: * need to be removed to register the new ContentHandler
0515: */
0516: static ContentHandlerImpl checkConflicts(ContentHandlerImpl handler)
0517: throws ContentHandlerException {
0518: ContentHandlerImpl[] handlers = RegistryStore
0519: .findConflicted(handler.ID);
0520: ContentHandlerImpl existing = null;
0521:
0522: if (handlers != null) {
0523: switch (handlers.length) {
0524: case 0:
0525: break;
0526: case 1:
0527: if (handler.classname.equals(handlers[0].classname)) {
0528: existing = handlers[0];
0529: break;
0530: }
0531: default:
0532: throw new ContentHandlerException(
0533: "ID would be ambiguous: " + handler.ID,
0534: ContentHandlerException.AMBIGUOUS);
0535: }
0536: }
0537:
0538: if (existing == null) {
0539: existing = RegistryStore.getHandler(handler.storageId,
0540: handler.classname);
0541: }
0542:
0543: return existing;
0544: }
0545:
0546: /**
0547: * Gets all of the content types for which there are registered
0548: * handlers.
0549: * After a successful registration, the content handler's type(s),
0550: * if any, will appear in this list.
0551: * <P>
0552: * Only content handlers that this application is
0553: * allowed to access will be included.</p>
0554: *
0555: * @return an array of types; MUST NOT be <code>null</code>
0556: */
0557: public String[] getTypes() {
0558: return RegistryStore.getValues(getID(),
0559: RegistryStore.FIELD_TYPES);
0560: }
0561:
0562: /**
0563: * Gets all of the IDs of the registered content handlers.
0564: * <P>
0565: * Only content handlers that this application is
0566: * allowed to access will be included.</p>
0567: * @return an array of content handler IDs;
0568: * MUST NOT be <code>null</code>
0569: */
0570: public String[] getIDs() {
0571: return RegistryStore.getValues(getID(), RegistryStore.FIELD_ID);
0572: }
0573:
0574: /**
0575: * Gets all of the actions of the registered content handlers.
0576: * After a successful registration the content handler's action(s),
0577: * if any, will appear in this list.
0578: * <P>
0579: * Only content handlers that this application is
0580: * allowed to access will be included.</p>
0581: * @return an array of content handler actions;
0582: * MUST NOT be <code>null</code>
0583: */
0584: public String[] getActions() {
0585: return RegistryStore.getValues(getID(),
0586: RegistryStore.FIELD_ACTIONS);
0587: }
0588:
0589: /**
0590: * Gets all of the suffixes of the registered content handlers.
0591: * After a successful registration the content handler's suffix(es),
0592: * if any, will appear in this list.
0593: * <P>
0594: * Only content handlers that this application is
0595: * allowed to access will be included.</p>
0596: * @return an array of content handler suffixes;
0597: * MUST NOT be <code>null</code>
0598: */
0599: public String[] getSuffixes() {
0600: return RegistryStore.getValues(getID(),
0601: RegistryStore.FIELD_SUFFIXES);
0602: }
0603:
0604: /**
0605: * Removes the content handler registration for the application
0606: * class and any bindings to the content handler name, content
0607: * type(s), suffix(es), action(s), and access restrictions.
0608: *
0609: * @param classname the name of the content handler class
0610: * @return if the content handler was
0611: * successfully removed <code>true</code> is returned,
0612: * <code>false</code> otherwise
0613: * @exception NullPointerException if <code>classname</code> is
0614: * <code>null</code>
0615: */
0616: public boolean unregister(String classname) {
0617: if (classname == null) {
0618: throw new NullPointerException(
0619: "classname argument can not be null");
0620: }
0621:
0622: synchronized (mutex) {
0623:
0624: ContentHandlerImpl curr = null;
0625: RegistryImpl reg = (RegistryImpl) registries.get(classname);
0626:
0627: if (reg != null) {
0628: curr = reg.getServer();
0629: } else {
0630: try {
0631: curr = RegistryStore.getHandler(application
0632: .getStorageId(), classname);
0633: } catch (IllegalArgumentException iae) {
0634: // Empty class name falls down without further processing.
0635: }
0636: }
0637:
0638: if (curr != null) {
0639: RegistryStore.unregister(curr.getID());
0640: int suiteId = application.getStorageId();
0641: if (reg != null && classname.equals(curr.classname)
0642: && suiteId == curr.storageId) {
0643: reg.handlerImpl = null;
0644: }
0645: return true;
0646: }
0647: }
0648:
0649: return false;
0650: }
0651:
0652: /**
0653: * Checks the Invocation and uses the ID, type, URL, and action,
0654: * if present, to find a matching ContentHandler and queues this
0655: * request to it.
0656: * <p>
0657: * If the <code>previous</code> Invocation is <code>null</code>, then
0658: * a new transaction is created; otherwise, this
0659: * Invocation will use the same transaction as the
0660: * <code>previous</code> Invocation.
0661: * <p>
0662: * The status of this Invocation MUST be <code>INIT</code>.
0663: * If there is a previous Invocation, that Invocation MUST
0664: * have a status of <code>ACTIVE</code>.
0665: * <p>
0666: * Candidate content handlers are found as described in
0667: * {@link #findHandler findHandler}. If any handlers are
0668: * found, one is selected for this Invocation.
0669: * The choice of content handler is implemention dependent.
0670: * <p>
0671: * If there is a non-null <code>previous</code> Invocation,
0672: * its status is set to <code>HOLD</code>.
0673: * A copy of the Invocation is made, the status is set to
0674: * <code>ACTIVE</code> and then queued to the
0675: * target content handler.
0676: * If the invoked content handler is not running, it MUST be started
0677: * as described in <a href="#execution">Invocation Processing</a>.
0678: *
0679: * <p>
0680: * The calling thread blocks while the content handler is being determined.
0681: * If a network access is needed, there may be an associated delay.
0682: *
0683: * @param invocation the Invocation containing the target ID, type,
0684: * actions, arguments, and responseRequired parameters;
0685: * MUST NOT be <code>null</code>
0686: * @param previous a previous Invocation for this Invocation;
0687: * may be <code>null</code>
0688: *
0689: * @return <code>true</code> if the application MUST first
0690: * voluntarily exit before the content handler can be started;
0691: * <code>false</code> otherwise
0692: *
0693: * @exception IllegalArgumentException is thrown if:
0694: * <ul>
0695: * <li> the ID, type, URL, and action are all
0696: * <code>null</code>, or </li>
0697: * <li> the argument array contains any <code>null</code>
0698: * references</li>
0699: * </ul>
0700: * @exception IOException is thrown if access to the content fails
0701: * @exception ContentHandlerException is thrown with a reason of:
0702: * <ul>
0703: * <li><code>TYPE_UNKNOWN</code> if the type
0704: * is not set and cannot be determined from the URL, or</li>
0705: * <li><code>NO_REGISTERED_HANDLER</code> if
0706: * there is no registered content handler that
0707: * matches the requested ID, type, url or actions.
0708: * </li>
0709: * </ul>
0710: * @exception IllegalStateException is thrown if the status of this
0711: * Invocation is not <code>INIT</code> or if the status of the
0712: * previous Invocation, if any, is not <code>ACTIVE</code>
0713: * @exception NullPointerException is thrown if the
0714: * <code>invocation</code> is <code>null</code>
0715: * @exception SecurityException if an invoke operation is not permitted
0716: */
0717: public boolean invoke(InvocationImpl invocation,
0718: InvocationImpl previous) throws IllegalArgumentException,
0719: IOException, ContentHandlerException {
0720: synchronized (mutex) {
0721: // Locate the content handler for this Invocation.
0722: ContentHandlerImpl handler = (ContentHandlerImpl) findHandler(invocation)[0];
0723:
0724: // Fill in information about the invoking application
0725: invocation.invokingID = getID();
0726: invocation.invokingSuiteId = application.getStorageId();
0727: invocation.invokingClassname = application.getClassname();
0728: invocation.invokingAuthority = application.getAuthority();
0729: invocation.invokingAppName = application
0730: .getApplicationName();
0731:
0732: boolean shouldExit = invocation.invoke(previous, handler);
0733: // Remember the invoked invocation for getResponse
0734: insertActive(invocation);
0735:
0736: return shouldExit;
0737: }
0738: }
0739:
0740: /**
0741: * Reinvokes the Invocation and uses the ID, type, URL, and action
0742: * to find a matching ContentHandler and re-queues this request to
0743: * it. Reinvocation is used to delegate the handling of an active
0744: * Invocation to another content handler.
0745: * The processing of the Invocation instance is complete and the
0746: * status is set to <code>OK</code>. Responses to the
0747: * reinvocation will be queued to the original invoking
0748: * application, if a response is required.
0749: *
0750: * <p>
0751: * Candidate content handlers are found as described in
0752: * {@link #findHandler findHandler}. If any handlers are
0753: * found, one is selected for this Invocation.
0754: * The choice of content handler is implementation dependent.
0755: * <p>
0756: * The status of this Invocation is set to <code>OK</code>.
0757: * A copy of the Invocation is made, the status is set to
0758: * <code>ACTIVE</code>, and then queued to the
0759: * target content handler.
0760: * If the invoked content handler application is not running,
0761: * it MUST be started
0762: * as described in <a href="#execution">Invocation Processing</a>.
0763: *
0764: * <p>
0765: * The calling thread blocks while the content handler is being determined.
0766: * If a network access is needed there may be an associated delay.
0767: *
0768: * @param invocation an Invocation containing the target ID, type,
0769: * action, arguments, and responseRequired parameters;
0770: * MUST NOT be <code>null</code>
0771: *
0772: * @return <code>true</code> if the application MUST first
0773: * voluntarily exit before the content handler can be started;
0774: * <code>false</code> otherwise
0775: *
0776: * @exception IllegalArgumentException is thrown if:
0777: * <ul>
0778: * <li> the ID, type, and URL are all <code>null</code>, or </li>
0779: * <li> the argument array contains any <code>null</code>
0780: * references</li>
0781: * </ul>
0782: * @exception IOException is thrown if access to the content fails
0783: * @exception ContentHandlerException is thrown with a reason of:
0784: * <code>NO_REGISTERED_HANDLER</code> if
0785: * there is no registered content handler that
0786: * matches the requested ID, type, URL, and action
0787: *
0788: * @exception NullPointerException is thrown if the
0789: * <code>invocation</code> is <code>null</code>
0790: * @exception SecurityException if an invoke operation is not
0791: * permitted or if access to the content is not permitted
0792: */
0793: public boolean reinvoke(InvocationImpl invocation)
0794: throws IllegalArgumentException, IOException,
0795: ContentHandlerException, SecurityException {
0796: synchronized (mutex) {
0797: // Locate the content handler for this Invocation.
0798: ContentHandlerImpl handler = (ContentHandlerImpl) findHandler(invocation)[0];
0799:
0800: // Save the TID in case the invoke fails
0801: int tid = invocation.tid;
0802:
0803: // The information about the invoking application is already set
0804: boolean shouldExit = invocation.invoke(null, handler);
0805:
0806: /*
0807: * Only if the invoke succeeds can the original Invocation be
0808: * discarded.
0809: * Restore the tid so the correct native invoc is disposed.
0810: */
0811: invocation.tid = tid;
0812: invocation.setStatus(InvocationImpl.DISPOSE);
0813: invocation.setStatus(Invocation.OK);
0814:
0815: return shouldExit;
0816: }
0817: }
0818:
0819: /**
0820: * Gets the next Invocation response pending for this application.
0821: * The method blocks until an Invocation response is available, but
0822: * not for longer than the timeout period.
0823: * The method can be unblocked with a call to
0824: * {@link #cancelGetResponse}.
0825: * The application can process the Invocation based on
0826: * its status. The status is one of
0827: * <code>OK</code>, <code>CANCELLED</code>, or <code>ERROR</code>.
0828: * <p>
0829: * If the Invocation was invoked with
0830: * {@link #invoke(InvocationImpl invocation, InvocationImpl
0831: * previous)},
0832: * the <code>getPrevious</code> method MUST return the
0833: * previous Invocation.
0834: * If the status of the previous Invocation is <code>HOLD</code>
0835: * then its status is restored to <code>ACTIVE</code>.
0836: *
0837: * <p>
0838: * If the original Invocation instance is reachable, then it
0839: * MUST be updated with the values from the response
0840: * and be returned to the application. If it is not
0841: * reachable, then a new instance is returned from getResponse
0842: * with the response values.
0843: *
0844: * @param wait <code>true</code> if the method
0845: * MUST wait for an Invocation if one is not currently available;
0846: * otherwise <code>false</code>
0847: * @param resp an InvocationImpl to fill in with the response
0848: *
0849: * @exception IllegalArgumentException if the context is not valid
0850: *
0851: * @return the next pending response Invocation or <code>null</code>
0852: * if the timeout expires and no Invocation is available or
0853: * if cancelled with {@link #cancelGetResponse}
0854: * @see #invoke
0855: * @see #cancelGetResponse
0856: */
0857: public Invocation getResponse(boolean wait, InvocationImpl resp) {
0858: // Application has tried to get a response; reset cleanup flags on all
0859: if (responseCalls == 0) {
0860: InvocationStore.setCleanup(application.getStorageId(),
0861: application.getClassname(), false);
0862: }
0863: responseCalls++;
0864:
0865: // Find a response for this application and context
0866: InvocationImpl invoc = InvocationStore.getResponse(resp,
0867: application.getStorageId(), application.getClassname(),
0868: wait);
0869: if (invoc != null) {
0870: // Keep track of how many responses have been recevied;
0871:
0872: /*
0873: * If there was a previous Request/Tid
0874: * find or create the previous Invocation
0875: * and update its state.
0876: */
0877: InvocationImpl existing = removeActive(invoc);
0878: if (existing != null) {
0879: /*
0880: * Copy mutable fields to the existing Invocation
0881: * Continue with the pre-existing Invocation
0882: */
0883: existing.ID = invoc.ID;
0884: existing.arguments = invoc.arguments;
0885: existing.data = invoc.data;
0886: existing.url = invoc.url;
0887: existing.type = invoc.type;
0888: existing.action = invoc.action;
0889: existing.status = invoc.status;
0890: invoc = existing;
0891: } else {
0892: // If there is a previousTid then restore the previous
0893: if (invoc.previousTid != 0) {
0894: /*
0895: * There will be a previous Invocation unless the app has
0896: * already finished it. It will have a HOLD status.
0897: */
0898: invoc.previous = InvocationStore.getByTid(
0899: invoc.previousTid, 0);
0900: }
0901: }
0902: if (invoc.previous != null
0903: && invoc.previous.status == Invocation.HOLD) {
0904: // Restore ACTIVE status to a previously HELD Invocation
0905: invoc.previous.setStatus(Invocation.ACTIVE);
0906: }
0907:
0908: // Make an attempt to gain the foreground
0909: if (invoc.invokingSuiteId != MIDletSuite.UNUSED_SUITE_ID
0910: && invoc.invokingClassname != null) {
0911:
0912: // Strong FG transition requested
0913: application.requestForeground(invoc.invokingSuiteId,
0914: invoc.invokingClassname);
0915: }
0916:
0917: return invoc.invocation;
0918: }
0919: return null;
0920: }
0921:
0922: /**
0923: * Cancels a pending <code>getResponse</code>.
0924: * This method will force a Thread blocked in a call to the
0925: * <code>getResponse</code> method for the same application
0926: * context to return early.
0927: * If no Thread is blocked; this call has no effect.
0928: */
0929: public void cancelGetResponse() {
0930: InvocationStore.cancel();
0931: }
0932:
0933: /**
0934: * Sets the listener to be notified when a new response is
0935: * available for the application context. The request must
0936: * be retrieved using {@link #getResponse getResponse}.
0937: *
0938: * @param listener the listener to register;
0939: * <code>null</code> to remove the listener.
0940: */
0941: public void setListener(ResponseListener listener) {
0942:
0943: // Create or update the listener implementation
0944: synchronized (this ) {
0945: if (listener != null || listenerImpl != null) {
0946: // Create or update the active listener thread
0947: if (listenerImpl == null) {
0948: listenerImpl = new ResponseListenerImpl(this ,
0949: listener);
0950: } else {
0951: listenerImpl.setListener(listener);
0952: }
0953:
0954: // If the listener thread no longer needed; clear it
0955: if (listener == null) {
0956: listenerImpl = null;
0957: }
0958: }
0959: }
0960: }
0961:
0962: /**
0963: * Gets the registered content handlers that could be used for
0964: * this Invocation. Only handlers accessible to the application
0965: * are considered. The values for ID, type, URL, and
0966: * action are used in the following order:
0967: * <ul>
0968: * <li>If the ID is non-null, then the set of candidate
0969: * handlers is determined from the {@link #forID forID}
0970: * method with the parameter <tt>exact</tt> set to false.
0971: * If there is an exact match it MUST be returned as
0972: * the first handler.
0973: * The type and URL are ignored. If there are no handlers that match
0974: * the requested ID then a <tt>ContentHandlerException</tt>
0975: * is thrown.</li>
0976: *
0977: * <li>If the ID and type are <code>null</code> and
0978: * the URL is <code>non-null</code> and
0979: * If the protocol supports typing of content, then
0980: * the type is determined
0981: * as described in {@link Invocation#findType}.
0982: * If the type cannot be determined from the content,
0983: * the type is set to <code>null</code>.</li>
0984: *
0985: * <li>If the ID is null and type is non-null,
0986: * then the set of candidate handlers is determined from the
0987: * {@link #forType forType} method.
0988: * If there are no handlers that match the requested type
0989: * then a <tt>ContentHandlerException</tt> is thrown. </li>
0990: *
0991: * <li>If both the ID and type are <code>null</code> and
0992: * the URL is <code>non-null</code> and
0993: * if the protocol does not support typing of content
0994: * or the type was not available from the content,
0995: * then the set of candidate handlers
0996: * includes any handler with a suffix that matches the
0997: * end of the path component of the URL.
0998: * If there are no handlers that match a registered
0999: * suffix then a <tt>ContentHandlerException</tt> is thrown.</li>
1000: *
1001: * <li>If the ID, type, and URL are all null, the set of candidate
1002: * handlers includes all of the accessible handlers.</li>
1003: *
1004: * <li>If the action is non-null, the set of candidate handlers
1005: * is reduced to contain only handlers that support the
1006: * action.</li>
1007: *
1008: * <li>If the set of candidate handlers is empty
1009: * then a <tt>ContentHandlerException</tt> is thrown.</li>
1010: * </ul>
1011: * <p>
1012: * The calling thread blocks while the ID and type are being determined.
1013: * If a network access is needed there may be an associated delay.
1014: *
1015: * @param invoc the ID, type, action, and URL that
1016: * are needed to identify one or more content handlers;
1017: * must not be <code>null</code>
1018: * @return an array of the <code>ContentHandler</code>(s)
1019: * that could be used for this Invocation; MUST NOT be <code>null</code>;
1020: *
1021: * @exception IOException is thrown if access to the content fails
1022: * @exception ContentHandlerException is thrown with a reason of
1023: * <code>NO_REGISTERED_HANDLER</code> if
1024: * there is no registered content handler that
1025: * matches the requested ID, type, URL, and action
1026: *
1027: * @exception IllegalArgumentException is thrown if ID, type, URL,
1028: * and action are all <code>null</code> or
1029: * if the content is accessed via the URL and the URL is invalid
1030: * @exception NullPointerException is thrown if the
1031: * <code>invocation</code> is <code>null</code>
1032: * @exception SecurityException is thrown if access to the content
1033: * is not permitted
1034: */
1035: public ContentHandler[] findHandler(InvocationImpl invoc)
1036: throws IOException, ContentHandlerException {
1037: ContentHandler[] handlers = null;
1038: if (invoc.getID() != null) {
1039: ContentHandler handler = forID(invoc.getID(), false);
1040: if (handler != null) {
1041: handlers = new ContentHandler[1];
1042: handlers[0] = handler;
1043: }
1044: } else {
1045: String action = invoc.getAction();
1046:
1047: // ID is null
1048: synchronized (mutex) {
1049: // Inhibit types change while doing lookups
1050: if (invoc.getType() == null && invoc.getURL() != null) {
1051: try {
1052: invoc.findType();
1053: } catch (ContentHandlerException che) {
1054: // Type is null
1055: }
1056: }
1057: if (invoc.getType() != null) {
1058: // The type is known; lookup the handlers
1059: handlers = forType(invoc.getType());
1060: } else if (invoc.getURL() != null) {
1061: /**
1062: * Call platform specific function for
1063: * getting handler by URL
1064: */
1065: ContentHandler suitable = RegistryStore.getByURL(
1066: getID(), invoc.getURL(), action);
1067:
1068: if (suitable != null) {
1069: handlers = new ContentHandler[1];
1070: handlers[0] = suitable;
1071: }
1072: } else if (action != null) {
1073: handlers = forAction(action);
1074: action = null;
1075: } else {
1076: throw new IllegalArgumentException(
1077: "not ID, type, URL, or action");
1078: }
1079:
1080: // Set of candidate handlers; check for matching action
1081: if (handlers != null && action != null) {
1082: int rem = 0; // number of handlers to remove
1083: for (int i = 0; i < handlers.length; i++) {
1084: if (!handlers[i].hasAction(action)) {
1085: handlers[i] = null;
1086: rem++;
1087: }
1088: }
1089: if (rem > 0) {
1090: int newsz = handlers.length - rem;
1091: if (newsz > 0) {
1092: ContentHandler[] newhand = new ContentHandler[newsz];
1093: int j;
1094: int k;
1095: for (j = k = 0; j < newsz; j++) {
1096: while (handlers[k] == null) {
1097: k++;
1098: }
1099: newhand[j] = handlers[k++];
1100: }
1101: handlers = newhand;
1102: } else {
1103: handlers = null;
1104: }
1105: }
1106: }
1107: }
1108: }
1109:
1110: if (handlers == null || handlers.length == 0) {
1111: throw new ContentHandlerException("no registered handler",
1112: ContentHandlerException.NO_REGISTERED_HANDLER);
1113: }
1114: return handlers;
1115: }
1116:
1117: /**
1118: * Gets the registered content handlers for the content type.
1119: * <P>
1120: * Only content handlers that are visible and accessible to this
1121: * application are returned.
1122: *
1123: * @param type the type of the requested content handlers
1124: * @return an array of the <code>ContentHandler</code>s registered
1125: * for the type; MUST NOT be <code>null</code>.
1126: * An empty array is returned if there are no
1127: * <code>ContentHandler</code>s accessible to
1128: * this application.
1129: * @exception NullPointerException if <code>type</code> is
1130: * <code>null</code>
1131: */
1132: public ContentHandler[] forType(String type) {
1133: return RegistryStore.findHandler(getID(),
1134: RegistryStore.FIELD_TYPES, type);
1135: }
1136:
1137: /**
1138: * Gets the registered content handlers that support the action.
1139: * <P>
1140: * Only content handlers that are visible and accessible to this
1141: * application are returned.
1142: *
1143: * @param action content handlers for which the action is supported
1144: * @return an array of the <code>ContentHandler</code>s registered
1145: * for the action; MUST NOT be <code>null</code>;
1146: * an empty array is returned if no <code>ContentHandler</code>s
1147: * are accessible to this application
1148: * @exception NullPointerException if <code>action</code> is
1149: * <code>null</code>
1150: */
1151: public ContentHandler[] forAction(String action) {
1152: return RegistryStore.findHandler(getID(),
1153: RegistryStore.FIELD_ACTIONS, action);
1154: }
1155:
1156: /**
1157: * Gets all of the content handlers for the suffix.
1158: * Only content handlers that are visible and accessible to this
1159: * application are returned.
1160: *
1161: * @param suffix the suffix to be used to get the associated
1162: * content handlers
1163: *
1164: * @return an array of the <code>ContentHandler</code>s registered
1165: * for the suffix; MUST NOT be <code>null</code>.
1166: * An empty array is returned if there are none accessible to
1167: * this application
1168: *
1169: * @exception NullPointerException if <code>suffix</code> is
1170: * <code>null</code>
1171: */
1172: public ContentHandler[] forSuffix(String suffix) {
1173: return RegistryStore.findHandler(getID(),
1174: RegistryStore.FIELD_SUFFIXES, suffix);
1175: }
1176:
1177: /**
1178: * Gets the registered content handler for the ID.
1179: * The query can be for an exact match or for the handler
1180: * matching the prefix of the requested ID.
1181: * <P>
1182: * Only a content handler which is visible to and accessible to this
1183: * application will be returned.
1184: *
1185: * @param ID the content handler application ID of the content
1186: * handler requested
1187: * @param exact <code>true</code> to require an exact match;
1188: * <code>false</code> to allow a registered content handler ID
1189: * to match a prefix of the requested ID
1190: *
1191: * @return the content handler that matches the ID,
1192: * otherwise <code>null</code>
1193: *
1194: * @exception NullPointerException if <code>ID</code> is
1195: * <code>null</code>
1196: */
1197: public ContentHandler forID(String ID, boolean exact) {
1198: return RegistryStore.getHandler(getID(), ID,
1199: exact ? RegistryStore.SEARCH_EXACT
1200: : RegistryStore.SEARCH_PREFIX);
1201: }
1202:
1203: /**
1204: * Gets the registered content handler for the
1205: * application class of this RegistryImpl.
1206: *
1207: * @return the content handler for the registered
1208: * <code>classname</code> if it was registered by this application.
1209: * Otherwise, it returns <code>null</code>.
1210: * @exception NullPointerException if <code>classname</code> is
1211: * <code>null</code>
1212: */
1213: public ContentHandlerImpl getServer() {
1214: return handlerImpl;
1215: }
1216:
1217: /**
1218: * Gets the content handler for the specified application class.
1219: * The classname must be a class in the current application.
1220: *
1221: * @param appl the application to look up a server fro
1222: * @return the content handler information for the registered
1223: * classname if the classname was registered by this application,
1224: * otherwise return <code>null</code>
1225: * @exception NullPointerException if <code>classname</code> is
1226: * <code>null</code>
1227: */
1228: ContentHandlerImpl getServer(AppProxy appl) {
1229: synchronized (mutex) {
1230:
1231: String classname = appl.getClassname();
1232: int storageId = appl.getStorageId();
1233:
1234: ContentHandlerImpl handler = RegistryStore.getHandler(
1235: storageId, classname);
1236:
1237: if (handler != null) {
1238: handler.appname = appl.getApplicationName();
1239: handler.version = appl.getVersion();
1240: handler.authority = appl.getAuthority();
1241: }
1242:
1243: return handler;
1244: }
1245: }
1246:
1247: /**
1248: * Gets the content handler ID for the current application.
1249: * The ID uniquely identifies the application which contains the
1250: * content handler.
1251: * The application ID is assigned when the application is installed.
1252: * If the application is a content handler then the ID must be
1253: * the content handler ID.
1254: * @return the ID; MUST NOT be <code>null</code>
1255: */
1256: public String getID() {
1257: return (handlerImpl != null) ? handlerImpl.getID()
1258: : application.getApplicationID();
1259: }
1260:
1261: /**
1262: * Insert an Invocation to the set of active Invocations.
1263: *
1264: * @param invoc an Invocation to add
1265: */
1266: private void insertActive(InvocationImpl invoc) {
1267: Integer tid = new Integer(invoc.tid);
1268: activeInvocations.put(tid, invoc);
1269: }
1270:
1271: /**
1272: * Remove an Invocation from the set of active Invocations.
1273: * @param invoc an Invocation to remvoe
1274: * @return the active Invocation or null if not found
1275: */
1276: private InvocationImpl removeActive(InvocationImpl invoc) {
1277: Integer tid = new Integer(invoc.tid);
1278: return (InvocationImpl) activeInvocations.remove(tid);
1279: }
1280:
1281: }
|