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.midp.content;
028:
029: import javax.microedition.content.ContentHandler;
030: import javax.microedition.content.ContentHandlerServer;
031: import javax.microedition.content.RequestListener;
032: import javax.microedition.content.ActionNameMap;
033: import javax.microedition.content.Invocation;
034:
035: import java.io.DataInputStream;
036: import java.io.DataOutputStream;
037:
038: /**
039: * The internal structure of a registered content handler.
040: */
041: public class ContentHandlerImpl implements ContentHandler {
042:
043: /**
044: * The content handler ID.
045: * Lengths up to 256 characters MUST be supported.
046: * The ID may be <code>null</code>.
047: */
048: String ID;
049:
050: /**
051: * The types that are supported by this content handler.
052: * If there are no types registered, the this field MUST either be
053: * <code>null</code> or refer to an empty array .
054: */
055: private String[] types;
056:
057: /**
058: * The suffixes of URLs that are supported by this content handler.
059: * If there are no suffixes, then this field MUST be <code>null</code>.
060: * The suffixes MUST include any and all punctuation. For example,
061: * the string <code>".png"</code>.
062: */
063: private String[] suffixes;
064:
065: /**
066: * The actions that are supported by this content handler.
067: * If there are no actions, then this field MSUT be <code>null</code>.
068: */
069: private String[] actions;
070:
071: /**
072: * The action names that are defined by this content handler.
073: */
074: ActionNameMap[] actionnames;
075:
076: /** The sequence number of this instance; monotonic increasing. */
077: final int seqno;
078:
079: /** The next sequence number to assign. */
080: private static int nextSeqno;
081:
082: /**
083: * The RequestListenerImpl; if a listener is set.
084: */
085: RequestListenerImpl listenerImpl;
086:
087: /** Empty String array to return when needed. */
088: public final static String[] ZERO_STRINGS = {};
089:
090: /** Empty ActionNameMap to return when needed. */
091: private final static ActionNameMap[] ZERO_ACTIONNAMES = new ActionNameMap[0];
092:
093: /** Property name for the current locale. */
094: private final static String LOCALE_PROP = "microedition.locale";
095:
096: /**
097: * The MIDlet suite storagename that contains the MIDlet.
098: */
099: int storageId;
100:
101: /**
102: * The Name from parsing the Property for the MIDlet
103: * with this classname.
104: */
105: String appname;
106:
107: /**
108: * The Version parsed from MIDlet-Version attribute.
109: */
110: String version;
111:
112: /**
113: * The application class name that implements this content
114: * handler. Note: Only the application that registered the class
115: * will see the classname; for other applications the value will be
116: * <code>null</code>.
117: */
118: String classname;
119:
120: /**
121: * The authority that authenticated this ContentHandler.
122: */
123: String authority;
124:
125: /**
126: * The accessRestrictions for this ContentHandler.
127: */
128: private String[] accessRestricted;
129:
130: /**
131: * Indicates content handler registration method:
132: * dynamic registration from the API, static registration from install
133: * or native content handler.
134: * Must be similar to enum in jsr211_registry.h
135: */
136: int registrationMethod;
137:
138: /** Content handler statically registered during installation */
139: final static int REGISTERED_STATIC = 0;
140: /** Dynamically registered content handler via API */
141: final static int REGISTERED_DYNAMIC = 1;
142: /** Native platform content handler */
143: final static int REGISTERED_NATIVE = 2;
144:
145: /** Count of requests retrieved via {@link #getRequest}. */
146: int requestCalls;
147:
148: /**
149: * Instance is a registration or unregistration.
150: * An unregistration needs only storageId and classname.
151: */
152: boolean removed;
153:
154: /**
155: * Construct a ContentHandlerImpl.
156: * Verifies that all strings are non-null
157: * @param types an array of types to register; may be
158: * <code>null</code>
159: * @param suffixes an array of suffixes to register; may be
160: * <code>null</code>
161: * @param actions an array of actions to register; may be
162: * <code>null</code>
163: * @param actionnames an array of ActionNameMaps to register; may be
164: * <code>null</code>
165: * @param ID the content handler ID; may be <code>null</code>
166: * @param accessRestricted the IDs of applications allowed access
167: * @param auth application authority
168: *
169: * @exception NullPointerException if any types, suffixes,
170: * actions, actionnames array element is null
171: *
172: * @exception IllegalArgumentException is thrown if any of
173: * the types, suffix, or action strings have a
174: * length of zero or
175: * if the ID has a length of zero or contains any
176: * control character or space (U+0000-U+00020)
177: */
178: ContentHandlerImpl(String[] types, String[] suffixes,
179: String[] actions, ActionNameMap[] actionnames, String ID,
180: String[] accessRestricted, String auth) {
181: this ();
182:
183: // Verify consistency between actions and ActionNameMaps
184: if (actionnames != null && actionnames.length > 0) {
185: if (actions == null) {
186: throw new IllegalArgumentException("no actions");
187: }
188: int len = actions.length;
189: for (int i = 0; i < actionnames.length; i++) {
190: // Verify the actions are the same
191: ActionNameMap map = actionnames[i];
192: if (len != map.size()) {
193: throw new IllegalArgumentException(
194: "actions not identical");
195: }
196:
197: for (int j = 0; j < len; j++) {
198: if (!actions[j].equals(map.getAction(j))) {
199: throw new IllegalArgumentException(
200: "actions not identical");
201: }
202: }
203:
204: /*
205: * Verify the locale of this ActionNameMap is not the same
206: * as any previous ActionNameMap.
207: */
208: for (int j = 0; j < i; j++) {
209: if (map.getLocale().equals(
210: actionnames[j].getLocale())) {
211: throw new IllegalArgumentException(
212: "duplicate locale");
213: }
214: }
215: }
216: }
217:
218: // Check the ID for invalid characters (controls or space)
219: if (ID != null) {
220: int len = ID.length();
221: if (len == 0) {
222: throw new IllegalArgumentException("invalid ID");
223: }
224: for (int i = 0; i < ID.length(); i++) {
225: if (ID.charAt(i) <= 0x0020) {
226: throw new IllegalArgumentException("invalid ID");
227: }
228: }
229: this .ID = ID;
230: }
231: this .types = copy(types);
232: this .suffixes = copy(suffixes);
233: this .actions = copy(actions);
234: this .actionnames = copy(actionnames);
235: this .accessRestricted = copy(accessRestricted);
236: this .authority = auth;
237: }
238:
239: /**
240: * Initialize a new instance with the same information.
241: * @param handler another ContentHandlerImpl
242: * @see javax.microedition.content.ContentHandlerServerImpl
243: */
244: protected ContentHandlerImpl(ContentHandlerImpl handler) {
245: this ();
246: types = handler.types;
247: suffixes = handler.suffixes;
248: ID = handler.ID;
249: accessRestricted = handler.accessRestricted;
250: actions = handler.actions;
251: actionnames = handler.actionnames;
252: listenerImpl = handler.listenerImpl;
253: storageId = handler.storageId;
254: classname = handler.classname;
255: version = handler.version;
256: registrationMethod = handler.registrationMethod;
257: requestCalls = handler.requestCalls;
258: authority = handler.authority;
259: appname = handler.appname;
260: }
261:
262: /**
263: * Constructor used to read handlers.
264: */
265: ContentHandlerImpl() {
266: seqno = nextSeqno++;
267: }
268:
269: /**
270: * Checks that all of the string references are non-null
271: * and not zero length. If either the argument is null or
272: * is an empty array the default ZERO length string array is used.
273: *
274: * @param strings array to check for null and length == 0
275: * @return a non-null array of strings; an empty array replaces null
276: * @exception NullPointerException if any string ref is null
277: * @exception IllegalArgumentException if any string
278: * has length == 0
279: */
280: public static String[] copy(String[] strings) {
281: if (strings != null && strings.length > 0) {
282: String[] copy = new String[strings.length];
283: for (int i = 0; i < strings.length; i++) {
284: if (strings[i].length() == 0) {
285: throw new IllegalArgumentException(
286: "string length is 0");
287: }
288: copy[i] = strings[i];
289:
290: }
291: return strings;
292: } else {
293: return ZERO_STRINGS;
294: }
295: }
296:
297: /**
298: * Checks that all of the actionname references are non-null.
299: *
300: * @param actionnames array to check for null and length == 0
301: * @return a non-null array of actionnames; an empty array replaces null
302: * @exception NullPointerException if any string ref is null
303: */
304: private static ActionNameMap[] copy(ActionNameMap[] actionnames) {
305: if (actionnames != null && actionnames.length > 0) {
306: ActionNameMap[] copy = new ActionNameMap[actionnames.length];
307: for (int i = 0; i < actionnames.length; i++) {
308: // Check for null
309: if (actionnames[i] == null) {
310: throw new NullPointerException();
311: }
312: copy[i] = actionnames[i];
313: }
314: return copy;
315: } else {
316: return ZERO_ACTIONNAMES;
317: }
318: }
319:
320: /**
321: * Copy an array of ContentHandlers making a new ContentHandler
322: * for each ContentHandler. Make copies of any mutiple object.
323: * @param handlers the array of handlers duplicate
324: * @return the new array of content handlers
325: */
326: public static ContentHandler[] copy(ContentHandler[] handlers) {
327: ContentHandler[] h = new ContentHandler[handlers.length];
328: for (int i = 0; i < handlers.length; i++) {
329: h[i] = handlers[i];
330: }
331: return h;
332: }
333:
334: /**
335: * Get the nth type supported by the content handler.
336: * @param index the index into the types
337: * @return the nth type
338: * @exception IndexOutOfBounds if index is less than zero or
339: * greater than or equal to the value of the
340: * {@link #getTypeCount getTypeCount} method.
341: */
342: public String getType(int index) {
343: return get(index, getTypes());
344: }
345:
346: /**
347: * Get the number of types supported by the content handler.
348: *
349: * @return the number of types
350: */
351: public int getTypeCount() {
352: return getTypes().length;
353: }
354:
355: /**
356: * Get types supported by the content handler.
357: *
358: * @return array of types supported
359: */
360: String[] getTypes() {
361: if (types == null) {
362: types = RegistryStore.getArrayField(ID,
363: RegistryStore.FIELD_TYPES);
364: }
365: return types;
366: }
367:
368: /**
369: * Determine if a type is supported by the content handler.
370: *
371: * @param type the type to check for
372: * @return <code>true</code> if the type is supported;
373: * <code>false</code> otherwise
374: * @exception NullPointerException if <code>type</code>
375: * is <code>null</code>
376: */
377: public boolean hasType(String type) {
378: return has(type, getTypes(), true);
379: }
380:
381: /**
382: * Get the nth suffix supported by the content handler.
383: * @param index the index into the suffixes
384: * @return the nth suffix
385: * @exception IndexOutOfBounds if index is less than zero or
386: * greater than or equal to the value of the
387: * {@link #getSuffixCount getSuffixCount} method.
388: */
389: public String getSuffix(int index) {
390: return get(index, getSuffixes());
391: }
392:
393: /**
394: * Get the number of suffixes supported by the content handler.
395: *
396: * @return the number of suffixes
397: */
398: public int getSuffixCount() {
399: return getSuffixes().length;
400: }
401:
402: /**
403: * Determine if a suffix is supported by the content handler.
404: *
405: * @param suffix the suffix to check for
406: * @return <code>true</code> if the suffix is supported;
407: * <code>false</code> otherwise
408: * @exception NullPointerException if <code>suffix</code>
409: * is <code>null</code>
410: */
411: public boolean hasSuffix(String suffix) {
412: return has(suffix, getSuffixes(), true);
413: }
414:
415: /**
416: * Get suffixes supported by the content handler.
417: *
418: * @return array of suffixes supported
419: */
420: String[] getSuffixes() {
421: if (suffixes == null) {
422: suffixes = RegistryStore.getArrayField(ID,
423: RegistryStore.FIELD_SUFFIXES);
424: }
425: return suffixes;
426: }
427:
428: /**
429: * Get the nth action supported by the content handler.
430: * @param index the index into the actions
431: * @return the nth action
432: * @exception IndexOutOfBounds if index is less than zero or
433: * greater than or equal to the value of the
434: * {@link #getActionCount getActionCount} method.
435: */
436: public String getAction(int index) {
437: return get(index, getActions());
438: }
439:
440: /**
441: * Get the number of actions supported by the content handler.
442: *
443: * @return the number of actions
444: */
445: public int getActionCount() {
446: return getActions().length;
447: }
448:
449: /**
450: * Determine if a action is supported by the content handler.
451: *
452: * @param action the action to check for
453: * @return <code>true</code> if the action is supported;
454: * <code>false</code> otherwise
455: * @exception NullPointerException if <code>action</code>
456: * is <code>null</code>
457: */
458: public boolean hasAction(String action) {
459: return has(action, getActions(), false);
460: }
461:
462: /**
463: * Get actions supported by the content handler.
464: *
465: * @return array of actions supported
466: */
467: String[] getActions() {
468: if (actions == null) {
469: actions = RegistryStore.getArrayField(ID,
470: RegistryStore.FIELD_ACTIONS);
471: }
472: return actions;
473: }
474:
475: /**
476: * Gets the value at index in the string array.
477: * @param index of the value
478: * @param strings array of strings to get from
479: * @return string at index.
480: * @exception IndexOutOfBounds if index is less than zero or
481: * greater than or equal length of the array.
482: */
483: private String get(int index, String[] strings) {
484: if (index < 0 || index >= strings.length) {
485: throw new IndexOutOfBoundsException();
486: }
487: return strings[index];
488: }
489:
490: /**
491: * Determines if the string is in the array.
492: * @param string to locate
493: * @param strings array of strings to get from
494: * @param ignoreCase true to ignore case in matching
495: * @return <code>true</code> if the value is found
496: * @exception NullPointerException if <code>string</code>
497: * is <code>null</code>
498: */
499: private boolean has(String string, String[] strings,
500: boolean ignoreCase) {
501: int len = string.length(); // Throw NPE if null
502: for (int i = 0; i < strings.length; i++) {
503: if (strings[i].length() == len
504: && string.regionMatches(ignoreCase, 0, strings[i],
505: 0, len)) {
506: return true;
507: }
508: }
509: return false;
510: }
511:
512: /**
513: * Get the mapping of actions to action names for the current
514: * locale supported by this content handler. The behavior is
515: * the same as invoking {@link #getActionNameMap} with the current
516: * locale.
517: *
518: * @return an ActionNameMap; if there is no map available for the
519: * current locale, then it MUST be <code>null</code>
520: */
521: public ActionNameMap getActionNameMap() {
522: String locale = System.getProperty(LOCALE_PROP);
523: return (locale == null) ? null : getActionNameMap(locale);
524: }
525:
526: /**
527: * Get the mapping of actions to action names for the requested
528: * locale supported by this content handler.
529: * The locale is matched against the available locales.
530: * If a match is found it is used. If an exact match is
531: * not found, then the locale string is shortened and retried
532: * if either of the "_" or "-" delimiters is present.
533: * The locale is shortened by retaining only the characters up to
534: * but not including the last occurence of the delimiter
535: * (either "_" or "-").
536: * The shortening and matching is repeated as long as the string
537: * contains one of the delimiters.
538: * Effectively, this will try the full locale and then try
539: * without the variant or country code, if they were present.
540: *
541: * @param locale for which to find an ActionNameMap;
542: * MUST NOT be <code>null</code>
543: * @return an ActionNameMap; if there is no map available for the
544: * locale, then it MUST be <code>null</code>
545: * @exception NullPointerException if the locale is <code>null</code>
546: */
547: public ActionNameMap getActionNameMap(String locale) {
548: while (locale.length() > 0) {
549: for (int i = 0; i < getActionNames().length; i++) {
550: if (locale.equals(getActionNames()[i].getLocale())) {
551: return getActionNames()[i];
552: }
553: }
554: int lastdash = locale.lastIndexOf('-');
555: if (lastdash < 0) {
556: break;
557: }
558: locale = locale.substring(0, lastdash);
559: }
560: return null;
561: }
562:
563: /**
564: * Gets the number of action name maps supported by the content handler.
565: *
566: * @return the number of action name maps
567: */
568: public int getActionNameMapCount() {
569: return getActionNames().length;
570: }
571:
572: /**
573: * Gets the n<sup>th</sup> ActionNameMap supported by the
574: * content handler.
575: * @param index the index of the locale
576: * @return the n<sup>th</sup> ActionNameMap
577: *
578: * @exception IndexOutOfBoundsException if index is less than zero or
579: * greater than or equal to the value of the
580: * {@link #getActionNameMapCount getActionNameMapCount} method.
581: */
582: public ActionNameMap getActionNameMap(int index) {
583: if (index < 0 || index >= getActionNames().length) {
584: throw new IndexOutOfBoundsException();
585: }
586: return getActionNames()[index];
587: }
588:
589: /**
590: * Get actions names for the content handler.
591: *
592: * @return array of actions names
593: */
594: private ActionNameMap[] getActionNames() {
595: if (actionnames == null) {
596: String[] locales = RegistryStore.getArrayField(ID,
597: RegistryStore.FIELD_LOCALES);
598: String[] names = RegistryStore.getArrayField(ID,
599: RegistryStore.FIELD_ACTION_MAP);
600:
601: actionnames = new ActionNameMap[locales.length];
602: for (int index = 0; index < locales.length; index++) {
603: String[] temp = new String[getActions().length];
604:
605: System.arraycopy(names, index * getActions().length,
606: temp, 0, getActions().length);
607:
608: actionnames[index] = new ActionNameMap(getActions(),
609: temp, locales[index]);
610: }
611: }
612: return actionnames;
613: }
614:
615: /**
616: * Returns the name used to present this content handler to a user.
617: * The value is extracted from the normal installation information
618: * about the content handler application.
619: *
620: * @return the user-friendly name of the application;
621: * it MUST NOT be <code>null</code>
622: */
623: public String getAppName() {
624: loadAppData();
625: return appname;
626: }
627:
628: /**
629: * Gets the version number of this content handler.
630: * The value is extracted from the normal installation information
631: * about the content handler application.
632: * @return the version number of the application;
633: * MAY be <code>null</code>
634: */
635: public String getVersion() {
636: loadAppData();
637: return version;
638: }
639:
640: /**
641: * Get the content handler ID. The ID uniquely identifies the
642: * application which contains the content handler.
643: * After registration and for every registered handler,
644: * the ID MUST NOT be <code>null</code>.
645: * @return the ID; MUST NOT be <code>null</code> unless the
646: * ContentHandler is not registered.
647: */
648: public String getID() {
649: return ID;
650: }
651:
652: /**
653: * Gets the name of the authority that authorized this application.
654: * This value MUST be <code>null</code> unless the device has been
655: * able to authenticate this application.
656: * If <code>non-null</code>, it is the string identifiying the
657: * authority. For example,
658: * if the application was a signed MIDlet, then this is the
659: * "subject" of the certificate used to sign the application.
660: * <p>The format of the authority for X.509 certificates is defined
661: * by the MIDP Printable Representation of X.509 Distinguished
662: * Names as defined in class
663: * <code>javax.microedition.pki.Certificate</code>. </p>
664: *
665: * @return the authority; may be <code>null</code>
666: */
667: public String getAuthority() {
668: loadAppData();
669: return authority;
670: }
671:
672: /**
673: * Initializes fields retrieved from AppProxy 'by-demand'.
674: */
675: private void loadAppData() {
676: if (appname == null) {
677: try {
678: AppProxy app = AppProxy.getCurrent().forApp(storageId,
679: classname);
680: appname = app.getApplicationName();
681: version = app.getVersion();
682: authority = app.getAuthority();
683: } catch (Throwable t) {
684: }
685: if (appname == null) {
686: appname = "";
687: }
688: }
689: }
690:
691: /**
692: * Gets the n<sup>th</sup> ID of an application or content handler
693: * allowed access to this content handler.
694: * The ID returned for each index must be the equal to the ID
695: * at the same index in the <tt>accessAllowed</tt> array passed to
696: * {@link javax.microedition.content.Registry#register Registry.register}.
697: *
698: * @param index the index of the ID
699: * @return the n<sup>th</sup> ID
700: * @exception IndexOutOfBoundsException if index is less than zero or
701: * greater than or equal to the value of the
702: * {@link #accessAllowedCount accessAllowedCount} method.
703: */
704: public String getAccessAllowed(int index) {
705: return get(index, getAccessRestricted());
706: }
707:
708: /**
709: * Gets the number of IDs allowed access by the content handler.
710: * The number of IDs must be equal to the length of the array
711: * of accessRestricted passed to
712: * {@link javax.microedition.content.Registry#register Registry.register}.
713: * If the number of IDs is zero then all applications and
714: * content handlers are allowed access.
715: *
716: * @return the number of accessRestricteds
717: */
718: public int accessAllowedCount() {
719: return getAccessRestricted().length;
720: }
721:
722: /**
723: * Determines if an ID MUST be allowed access by the content handler.
724: * Access MUST be allowed if the ID has a prefix that exactly matches
725: * any of the IDs returned by {@link #getAccessAllowed}.
726: * The prefix comparison is equivalent to
727: * <code>java.lang.String.startsWith</code>.
728: *
729: * @param ID the ID for which to check access
730: * @return <code>true</code> if access MUST be allowed by the
731: * content handler;
732: * <code>false</code> otherwise
733: * @exception NullPointerException if <code>accessRestricted</code>
734: * is <code>null</code>
735: */
736: public boolean isAccessAllowed(String ID) {
737: ID.length(); // check for null
738: if (getAccessRestricted().length == 0) {
739: return true;
740: }
741: for (int i = 0; i < getAccessRestricted().length; i++) {
742: if (ID.startsWith(getAccessRestricted()[i])) {
743: return true;
744: }
745: }
746: return false;
747: }
748:
749: /**
750: * Get accesses for the content handler.
751: *
752: * @return array of allowed class names
753: */
754: private String[] getAccessRestricted() {
755: if (accessRestricted == null) {
756: accessRestricted = RegistryStore.getArrayField(ID,
757: RegistryStore.FIELD_ACCESSES);
758: }
759: return accessRestricted;
760: }
761:
762: /**
763: * Gets the next Invocation request pending for this
764: * ContentHandlerServer.
765: * The method can be unblocked with a call to
766: * {@link #cancelGetRequest cancelGetRequest}.
767: * The application should process the Invocation as
768: * a request to perform the <code>action</code> on the content.
769: *
770: * @param wait <code>true</code> if the method must wait for
771: * for an Invocation if one is not available;
772: * <code>false</code> if the method MUST NOT wait.
773: * @param invocation an Invocation instance that will delegate to
774: * the result; if any
775: * @return the next pending Invocation or <code>null</code>
776: * if no Invocation is available; <code>null</code>
777: * if cancelled with {@link #cancelGetRequest cancelGetRequest}
778: * @see javax.microedition.content.Registry#invoke
779: * @see javax.microedition.content.ContentHandlerServer#finish
780: */
781: public InvocationImpl getRequest(boolean wait, Invocation invocation) {
782: // Application has tried to get a request; reset cleanup flags on all
783: if (requestCalls == 0) {
784: InvocationStore.setCleanup(storageId, classname, false);
785: }
786: requestCalls++;
787:
788: InvocationImpl invoc = InvocationStore.getRequest(storageId,
789: classname, wait);
790: if (invoc != null) {
791: // Keep track of number of requests delivered to the application
792: AppProxy.requestForeground(invoc.invokingSuiteId,
793: invoc.invokingClassname, invoc.suiteId,
794: invoc.classname);
795: invoc.invocation = invocation;
796: }
797: return invoc;
798: }
799:
800: /**
801: * Cancel a pending <code>getRequest</code>.
802: * This method will force a Thread blocked in a call to the
803: * <code>getRequest</code> method for the same application
804: * context to return early.
805: * If no Thread is blocked; this call has no effect.
806: */
807: public void cancelGetRequest() {
808: InvocationStore.cancel();
809: }
810:
811: /**
812: * Finish this Invocation and set the status for the response.
813: * The <code>finish</code> method may only be called when this
814: * Invocation
815: * has a status of <code>ACTIVE</code> or <code>HOLD</code>.
816: * <p>
817: * The content handler may modify the URL, type, action, or
818: * arguments before invoking <code>finish</code>.
819: * If the method {@link Invocation#getResponseRequired} returns
820: * <code>true</code> then the modified
821: * values MUST be returned to the invoking application.
822: *
823: * @param invoc the Invocation to finish
824: * @param status the new status of the Invocation. This MUST be either
825: * <code>OK</code> or <code>CANCELLED</code>.
826: *
827: * @return <code>true</code> if the MIDlet suite MUST
828: * voluntarily exit before the response can be returned to the
829: * invoking application
830: *
831: * @exception IllegalArgumentException if the new
832: * <code>status</code> of the Invocation
833: * is not <code>OK</code> or <code>CANCELLED</code>
834: * @exception IllegalStateException if the current
835: * <code>status</code> of the
836: * Invocation is not <code>ACTIVE</code> or <code>HOLD</code>
837: * @exception NullPointerException if the invocation is <code>null</code>
838: */
839: protected boolean finish(InvocationImpl invoc, int status) {
840: int currst = invoc.getStatus();
841: if (currst != Invocation.ACTIVE && currst != Invocation.HOLD) {
842: throw new IllegalStateException("Status already set");
843: }
844: // If ACTIVE or HOLD it must be an InvocationImpl
845: return invoc.finish(status);
846: }
847:
848: /**
849: * Set the listener to be notified when a new request is
850: * available for this content handler. The request MUST
851: * be retrieved using {@link #getRequest}.
852: *
853: * @param listener the listener to register;
854: * <code>null</code> to remove the listener.
855: */
856: public void setListener(RequestListener listener) {
857: synchronized (this ) {
858: if (listener != null || listenerImpl != null) {
859: // Create or update the active listener thread
860: if (listenerImpl == null) {
861: listenerImpl = new RequestListenerImpl(this ,
862: listener);
863: } else {
864: listenerImpl.setListener(listener);
865: }
866:
867: // If the listener thread no longer needed; clear it
868: if (listener == null) {
869: listenerImpl = null;
870: }
871: }
872: }
873: }
874:
875: /**
876: * Notify the request listener of a pending request.
877: * Overridden by subclass.
878: */
879: protected void requestNotify() {
880: }
881:
882: /**
883: * Compare two ContentHandlerImpl's for equality.
884: * Classname, storageID, and seqno must match.
885: * @param other another ContentHandlerImpl
886: * @return true if the other handler is for the same class,
887: * storageID, and seqno.
888: */
889: boolean equals(ContentHandlerImpl other) {
890: return seqno == other.seqno && storageId == other.storageId
891: && classname.equals(other.classname);
892: }
893:
894: /**
895: * Debug routine to print the values.
896: * @return a string with the details
897: */
898: public String toString() {
899: if (AppProxy.LOG_INFO) {
900: StringBuffer sb = new StringBuffer(80);
901: sb.append("CH:");
902: sb.append(" classname: ");
903: sb.append(classname);
904: sb.append(", removed: ");
905: sb.append(removed);
906: sb.append(", flag: ");
907: sb.append(registrationMethod);
908: sb.append(", types: ");
909: toString(sb, types);
910: sb.append(", ID: ");
911: sb.append(ID);
912: sb.append(", suffixes: ");
913: toString(sb, suffixes);
914: sb.append(", actions: ");
915: toString(sb, actions);
916: sb.append(", access: ");
917: toString(sb, accessRestricted);
918: sb.append(", suiteID: ");
919: sb.append(storageId);
920: sb.append(", authority: ");
921: sb.append(authority);
922: sb.append(", appname: ");
923: sb.append(appname);
924: return sb.toString();
925: } else {
926: return super .toString();
927: }
928: }
929:
930: /**
931: * Append all of the strings inthe array to the string buffer.
932: * @param sb a StringBuffer to append to
933: * @param strings an array of strings.
934: */
935: private void toString(StringBuffer sb, String[] strings) {
936: if (strings == null) {
937: sb.append("null");
938: return;
939: }
940: for (int i = 0; i < strings.length; i++) {
941: if (i > 0) {
942: sb.append(':');
943: }
944: sb.append(strings[i]);
945: }
946: }
947: }
|