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 com.sun.midp.midlet.MIDletSuite;
030:
031: /**
032: * The store for pending Invocations.
033: * New Invocations are queued with {@link #put} method and
034: * retrieved with the {@link #get} method. The {@link #cancel}
035: * method is used to unblock calls to blocking {@link #get} methods.
036: * <p>
037: * Synchronization is performed by the native methods; access
038: * is serialized by the VM running in a single native thread and
039: * by NOT preempting native method calls.
040: * The native code uses the SNI ability to block a thread and
041: * unblock it at a later time. The implementation does not poll for
042: * requests but blocks, if requested, until it is unblocked.
043: */
044: class InvocationStore {
045:
046: /**
047: * The count of cancel requests; access is not synchronized because
048: * it is only incrementes in one place and it does not matter if it is
049: * incremented once or twice. A new cancel has occurred if
050: * the value has been incremented since an operation was started.
051: */
052: private static int cancelCount;
053:
054: /** The mode for get to retrieve a new request. */
055: private static final int MODE_REQUEST = 0;
056:
057: /** The mode for get to retrieve a new response. */
058: private static final int MODE_RESPONSE = 1;
059:
060: /** The mode for get to retrieve a new cleanup. */
061: private static final int MODE_CLEANUP = 2;
062:
063: /** The mode for listen for new unmarked request. */
064: private static final int MODE_LREQUEST = 3;
065:
066: /** The mode for listen for a new unmarked response. */
067: private static final int MODE_LRESPONSE = 4;
068:
069: /** The mode for get to retrieve a new ACTIVE, HOLD, or WAITING request. */
070: // private static final int MODE_PENDING = 5;
071: /** The mode for get to retreive byte <code>tid</code>. */
072: private static final int MODE_TID = 6;
073:
074: /** The mode to get the Invocation after <code>tid</code>. */
075: private static final int MODE_TID_NEXT = 7;
076:
077: /** The mode to get the Invocation before <code>tid</code>. */
078: private static final int MODE_TID_PREV = 8;
079:
080: /**
081: * Private constructor to prevent instance creation.
082: */
083: private InvocationStore() {
084: }
085:
086: /**
087: * Put a new Invocation into the store.
088: * It can be modified by {@link #setStatus}.
089: * The TID (transaction ID) is updated with a newly assigned value.
090: *
091: * @param invoc an InvocationImpl instance with the members properly
092: * initialized.
093: * @see #getRequest
094: * @see #getResponse
095: */
096: static void put(InvocationImpl invoc) {
097: if (invoc.suiteId == MIDletSuite.UNUSED_SUITE_ID
098: || invoc.classname == null) {
099: throw new NullPointerException();
100: }
101: put0(invoc);
102: if (AppProxy.LOG_INFO) {
103: AppProxy.getCurrent().logInfo("Store put0: " + invoc);
104: }
105: }
106:
107: /**
108: * Get a new InvocationImpl request from the store using a MIDlet
109: * suiteId and classname.
110: *
111: * @param suiteId the MIDlet suiteId to search for,
112: * MUST not be <code>null</code>
113: * @param classname to match, must not be null
114: * @param shouldBlock true if the method should block
115: * waiting for an Invocation
116: *
117: * @return <code>InvocationImpl</code> if any was found with
118: * the same MIDlet suiteId and classname with
119: * its status is set to ACTIVE;
120: * <code>null</code> is returned if there is no matching Invocation
121: */
122: static InvocationImpl getRequest(int suiteId, String classname,
123: boolean shouldBlock) {
124: InvocationImpl invoc = new InvocationImpl();
125: if (suiteId == MIDletSuite.UNUSED_SUITE_ID || classname == null) {
126: throw new NullPointerException();
127: }
128: invoc.suiteId = suiteId;
129: invoc.classname = classname;
130:
131: return get(invoc, MODE_REQUEST, shouldBlock);
132: }
133:
134: /**
135: * Get a new InvocationImpl response from the store using a
136: * MIDlet suiteId and classname.
137: * The response is removed from the store.
138: *
139: * @param invoc an InvocationImpl to fill with the response
140: * @param suiteId the MIDletSuite ID
141: * @param classname the classname
142: * @param shouldBlock true if the method should block
143: * waiting for an Invocation
144: *
145: * @return <code>InvocationImpl</code> if any was found with
146: * the same MIDlet suiteId and classname if one was requested;
147: * <code>null</code> is returned if there is no matching Invocation
148: */
149: static InvocationImpl getResponse(InvocationImpl invoc,
150: int suiteId, String classname, boolean shouldBlock) {
151: invoc.suiteId = suiteId;
152: invoc.classname = classname;
153:
154: return get(invoc, MODE_RESPONSE, shouldBlock);
155: }
156:
157: /**
158: * Performs cleanup for a ContentHandler
159: * by suiteId and classname.
160: * <p>
161: * Any marked {@link #setCleanup} invocations still in the queue
162: * are handled based on status:
163: * <UL>
164: * <li>ACTIVE Invocations are returned from this method
165: * so they can be have the ERROR status set and so the
166: * invoking application relaunched.</li>
167: * <li>INIT Invocations are requeued to the invoking application
168: * with ERROR status. </li>
169: * <li>OK, CANCELLED, ERROR, or INITIATED Invocations are
170: * discrded.</li>
171: * <li>HOLD status Invocations are retained pending
172: * completion of previous Invocation. TBD: Chained HOLDs...</li>
173: * </ul>
174: *
175: * @param suiteId the MIDletSuite ID
176: * @param classname the classname
177: *
178: * @return <code>InvocationImpl</code> if any was found with
179: * the same MIDlet suiteId and classname;
180: * <code>null</code> is returned if there is no matching Invocation
181: */
182: static InvocationImpl getCleanup(int suiteId, String classname) {
183: InvocationImpl invoc = new InvocationImpl();
184: invoc.suiteId = suiteId;
185: invoc.classname = classname;
186:
187: return get(invoc, MODE_CLEANUP, false);
188: }
189:
190: /**
191: * Get an Invocation from the store based on its <code>tid</code>.
192: * The normal state transitions and dispositions are NOT performed.
193: * If TID == 0 then the first tid is used as the reference.
194: * If TID == 0 and relative == 0 then null is returned.
195: * This method never waits.
196: *
197: * @param tid the <code>tid</code> to fetch
198: * @param relative -1, 0, +1 to get previous, equal, or next
199: * @return an InvocationImpl object if a matching tid was found;
200: * otherwise <code>null</code>
201: */
202: static InvocationImpl getByTid(int tid, int relative) {
203: InvocationImpl invoc = new InvocationImpl();
204: int mode = MODE_TID;
205: if (tid != 0) {
206: if (relative < 0) {
207: mode = MODE_TID_PREV;
208: } else if (relative > 0) {
209: mode = MODE_TID_NEXT;
210: }
211: }
212: invoc.suiteId = MIDletSuite.UNUSED_SUITE_ID;
213: invoc.classname = null;
214: invoc.tid = tid;
215: return get(invoc, mode, false);
216: }
217:
218: /**
219: * Get an InvocationImpl from the store using a MIDlet suiteId
220: * and classname.
221: * The mode controls whether getting an Invocation
222: * from the store removes it from the store.
223: *
224: * @param invoc InvocationImpl to fill in with result
225: * @param mode one of {@link #MODE_REQUEST}, {@link #MODE_RESPONSE},
226: * or {@link #MODE_CLEANUP}, {@link #MODE_LREQUEST},
227: * or {@link #MODE_LRESPONSE}, {@link #MODE_TID}.
228: * @param shouldBlock true if the method should block
229: * waiting for an Invocation
230: *
231: * @return <code>InvocationImpl</code> if any was found with
232: * the same MIDlet suiteId and classname if one was requested;
233: * <code>null</code> is returned if there is no matching Invocation
234: */
235: private static InvocationImpl get(InvocationImpl invoc, int mode,
236: boolean shouldBlock) {
237: String classname = invoc.classname;
238: invoc.setArgs(null);
239: invoc.setData(null);
240:
241: int s = 0;
242: int oldCancelCount = cancelCount;
243: while ((s = get0(invoc, invoc.suiteId, invoc.classname, mode,
244: shouldBlock)) != 1) {
245: if (s == -1) {
246: /*
247: * Sizes of arguments and data buffers were insufficient
248: * reallocate and retry.
249: */
250: invoc.setArgs(new String[invoc.argsLen]);
251: invoc.setData(new byte[invoc.dataLen]);
252: continue;
253: }
254: // Don't wait unless requested
255: if (!shouldBlock) {
256: break;
257: }
258: // No matching request; retry unless cancelled
259: if (cancelCount > oldCancelCount) {
260: // Was cancelled; s == 0 -> no Invocation
261: break;
262: }
263: }
264:
265: // Update the return if no invocation
266: if (s == 0) {
267: invoc = null;
268: }
269:
270: if (AppProxy.LOG_INFO) {
271: AppProxy.getCurrent().logInfo(
272: "Store get: " + classname + ", mode: " + mode
273: + ", " + invoc);
274: }
275: return invoc;
276: }
277:
278: /**
279: * Sets the status of an existing Invocation.
280: * If the status is OK, CANCELLED, ERROR, or INITIATED
281: * and a response is required then the invocation is
282: * requeued to the invoking application; if no response
283: * is required the request is discarded and the transaction id (tid)
284: * is set to zero.
285: *
286: * @param invoc an InvocationImpl previously retrieved with get
287: */
288: static void setStatus(InvocationImpl invoc) {
289: setStatus0(invoc);
290: if (AppProxy.LOG_INFO) {
291: AppProxy.getCurrent().logInfo("Store setStatus0: " + invoc);
292: }
293: }
294:
295: /**
296: * Updates the parameters of the invocation in the native store.
297: * The ID, URL, type, action, arguments, and data are
298: * stored again in native.
299: *
300: * @param invoc an InvocationImpl previously retrieved with get
301: */
302: static void setParams(InvocationImpl invoc) {
303: setParams0(invoc);
304: if (AppProxy.LOG_INFO) {
305: AppProxy.getCurrent().logInfo("Store setParams0: " + invoc);
306: }
307: }
308:
309: /**
310: * Listen for a matching invocation.
311: * When a matching invocation is present, true is returned.
312: * Each Invocation instance is only returned once.
313: * After it has been returned once; it is ignored subsequently.
314: *
315: * @param suiteId the MIDlet suiteId to search for,
316: * MUST not be <code>null</code>
317: * @param classname to match, must not be null
318: * @param request true to listen for a request; else a response
319: * @param shouldBlock true if the method should block
320: * waiting for an Invocation
321: *
322: * @return true if a matching invocation is present; false otherwise
323: */
324: static boolean listen(int suiteId, String classname,
325: boolean request, boolean shouldBlock) {
326: if (suiteId == MIDletSuite.UNUSED_SUITE_ID || classname == null) {
327: throw new NullPointerException();
328: }
329: int mode = (request ? MODE_LREQUEST : MODE_LRESPONSE);
330: boolean pending = false;
331:
332: int oldCancelCount = cancelCount;
333: while ((pending = listen0(suiteId, classname, mode, shouldBlock)) == false
334: && shouldBlock) {
335: // No pending request; retry unless cancelled
336: if (cancelCount > oldCancelCount) {
337: // Was cancelled; s == 0 -> no Invocation
338: break;
339: }
340: }
341:
342: if (AppProxy.LOG_INFO) {
343: AppProxy.getCurrent().logInfo(
344: "Store listen: " + classname + ", request: "
345: + request + ", pending: " + pending);
346: }
347: return pending;
348: }
349:
350: /**
351: * Reset the flags for requests or responses that are pending.
352: * Once reset, any pending requests or responses will be
353: * returned when listen0 is called.
354: *
355: * @param suiteId the MIDlet suiteId to search for,
356: * MUST not be <code>null</code>
357: * @param classname to match, must not be null
358: * @param request true to reset request notification flags;
359: * else reset response notification flags
360: */
361: static void setListenNotify(int suiteId, String classname,
362: boolean request) {
363: if (suiteId == MIDletSuite.UNUSED_SUITE_ID || classname == null) {
364: throw new NullPointerException();
365: }
366:
367: int mode = (request ? MODE_LREQUEST : MODE_LRESPONSE);
368: setListenNotify0(suiteId, classname, mode);
369:
370: if (AppProxy.LOG_INFO) {
371: AppProxy.getCurrent().logInfo(
372: "Store setListenNotify: " + classname
373: + ", request: " + request);
374: }
375: }
376:
377: /**
378: * Cancel a blocked {@link #get} or {@link #listen}
379: * method if it is blocked in the native code.
380: */
381: static void cancel() {
382: cancelCount++;
383: cancel0();
384: }
385:
386: /**
387: * Marks any existing invocations for the content handler.
388: * Any marked invocation will be modified by {@link #getCleanup}.
389: *
390: * @param suiteId the suite to mark
391: * @param classname the MIDlet within the suite
392: * @param cleanup <code>true</code> to mark the Invocation for
393: * cleanup at exit
394: */
395:
396: static void setCleanup(int suiteId, String classname,
397: boolean cleanup) {
398: if (AppProxy.LOG_INFO) {
399: AppProxy.getCurrent().logInfo(
400: "Store setCleanup: " + classname + ": " + cleanup);
401: }
402: setCleanup0(suiteId, classname, cleanup);
403: }
404:
405: /**
406: * Return the number of invocations in the native queue.
407: * @return the number of invocations in the native queue
408: */
409: static int size() {
410: return size0();
411: }
412:
413: /**
414: * Native method to store a new Invocation.
415: * All of the fields of the InvocationImpl are stored.
416: * @param invoc the InvocationImpl to store
417: */
418: private static native void put0(InvocationImpl invoc);
419:
420: /**
421: * Native method to fill an available InvocationImpl with an
422: * available stored Invocation with the status (if non-zero),
423: * the suiteId, classname in the prototype InvocationImpl.
424: * Any InvocationImpl with a matching status, suite and
425: * class will be returned.
426: * Depending on the mode the stored invocation will be removed
427: * from the store.
428: * @param invoc the Invocation containing the suiteId and
429: * classname to fill in with an available invocation.
430: * @param suiteId the MIDletSuite ID to match
431: * @param classname the classname to match
432: * @param mode one of {@link #MODE_REQUEST}, {@link #MODE_RESPONSE},
433: * or {@link #MODE_CLEANUP}
434: * @param shouldBlock True if the method should block until an
435: * Invocation is available
436: * @return 1 if a matching invocation was found and returned
437: * in its entirety; zero if there was no matching invocation;
438: * -1 if the sizes of the arguments or parameter array were wrong
439: * @see #get
440: */
441: private static native int get0(InvocationImpl invoc, int suiteId,
442: String classname, int mode, boolean shouldBlock);
443:
444: /**
445: * Sets the status of an existing Invocation
446: * and handles response required behavior.
447: *
448: * @param invoc an InvocationImpl previously retrieved with get.
449: */
450: private static native void setStatus0(InvocationImpl invoc);
451:
452: /**
453: * Updates the parameters of the invocation in the native store.
454: * The ID, URL, type, action, arguments, and data are
455: * stored again in native.
456: *
457: * @param invoc an InvocationImpl previously retrieved with get
458: */
459: private static native void setParams0(InvocationImpl invoc);
460:
461: /**
462: * Native method to listen for pending invocations with
463: * matching suite, classname, and status. Cancel() will
464: * also cause this method to return if blocked.
465: * Each Invocation will only be returned once to prevent
466: * multiple notifications.
467: *
468: * @param suiteId the MIDletSuite ID to match
469: * @param classname the classname to match
470: * @param mode one of {@link #MODE_LREQUEST}, {@link #MODE_LRESPONSE}
471: * @param shouldBlock true if the method should block until an
472: * Invocation is available
473: * @return true if a matching invocation was found; otherwise false.
474: * @see #get0
475: */
476: private static native boolean listen0(int suiteId,
477: String classname, int mode, boolean shouldBlock);
478:
479: /**
480: * Native method to reset the listen notified state for pending
481: * invocations with matching suite, classname and status.
482: * Each Invocation will only be returned once to prevent
483: * multiple notifications.
484: *
485: * @param suiteId the MIDletSuite ID to match
486: * @param classname the classname to match
487: * @param mode one of {@link #MODE_LREQUEST}, {@link #MODE_LRESPONSE}
488: * <code>false</code> to reset the notified state for responses
489: * @see #listen0
490: */
491: private static native void setListenNotify0(int suiteId,
492: String classname, int mode);
493:
494: /**
495: * Native method to unblock any threads that might be
496: * waiting for an invocation by way of having called
497: * {@link #get0}.
498: *
499: */
500: private static native void cancel0();
501:
502: /**
503: * Sets the cleanup flag in matching Invocations.
504: * Any marked invocation will be modified by {@link #getCleanup}.
505: *
506: * @param suiteId the MIDlet suiteId to search for,
507: * MUST not be <code>null</code>
508: * @param classname to match, must not be null
509: * @param cleanup <code>true</code> to mark the Invocation for
510: * cleanup at exit
511: */
512: private static native void setCleanup0(int suiteId,
513: String classname, boolean cleanup);
514:
515: /**
516: * Return the number of invocations in the native queue.
517: * @return the number of invocations in the native queue
518: */
519: private static native int size0();
520: }
|