/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.midp.content;
import com.sun.midp.midlet.MIDletSuite;
/**
* The store for pending Invocations.
* New Invocations are queued with {@link #put} method and
* retrieved with the {@link #get} method. The {@link #cancel}
* method is used to unblock calls to blocking {@link #get} methods.
* <p>
* Synchronization is performed by the native methods; access
* is serialized by the VM running in a single native thread and
* by NOT preempting native method calls.
* The native code uses the SNI ability to block a thread and
* unblock it at a later time. The implementation does not poll for
* requests but blocks, if requested, until it is unblocked.
*/
class InvocationStore {
/**
* The count of cancel requests; access is not synchronized because
* it is only incrementes in one place and it does not matter if it is
* incremented once or twice. A new cancel has occurred if
* the value has been incremented since an operation was started.
*/
private static int cancelCount;
/** The mode for get to retrieve a new request. */
private static final int MODE_REQUEST = 0;
/** The mode for get to retrieve a new response. */
private static final int MODE_RESPONSE = 1;
/** The mode for get to retrieve a new cleanup. */
private static final int MODE_CLEANUP = 2;
/** The mode for listen for new unmarked request. */
private static final int MODE_LREQUEST = 3;
/** The mode for listen for a new unmarked response. */
private static final int MODE_LRESPONSE = 4;
/** The mode for get to retrieve a new ACTIVE, HOLD, or WAITING request. */
// private static final int MODE_PENDING = 5;
/** The mode for get to retreive byte <code>tid</code>. */
private static final int MODE_TID = 6;
/** The mode to get the Invocation after <code>tid</code>. */
private static final int MODE_TID_NEXT = 7;
/** The mode to get the Invocation before <code>tid</code>. */
private static final int MODE_TID_PREV = 8;
/**
* Private constructor to prevent instance creation.
*/
private InvocationStore() {
}
/**
* Put a new Invocation into the store.
* It can be modified by {@link #setStatus}.
* The TID (transaction ID) is updated with a newly assigned value.
*
* @param invoc an InvocationImpl instance with the members properly
* initialized.
* @see #getRequest
* @see #getResponse
*/
static void put(InvocationImpl invoc) {
if (invoc.suiteId == MIDletSuite.UNUSED_SUITE_ID ||
invoc.classname == null) {
throw new NullPointerException();
}
put0(invoc);
if (AppProxy.LOG_INFO) {
AppProxy.getCurrent().logInfo("Store put0: " + invoc);
}
}
/**
* Get a new InvocationImpl request from the store using a MIDlet
* suiteId and classname.
*
* @param suiteId the MIDlet suiteId to search for,
* MUST not be <code>null</code>
* @param classname to match, must not be null
* @param shouldBlock true if the method should block
* waiting for an Invocation
*
* @return <code>InvocationImpl</code> if any was found with
* the same MIDlet suiteId and classname with
* its status is set to ACTIVE;
* <code>null</code> is returned if there is no matching Invocation
*/
static InvocationImpl getRequest(int suiteId, String classname,
boolean shouldBlock) {
InvocationImpl invoc = new InvocationImpl();
if (suiteId == MIDletSuite.UNUSED_SUITE_ID || classname == null) {
throw new NullPointerException();
}
invoc.suiteId = suiteId;
invoc.classname = classname;
return get(invoc, MODE_REQUEST, shouldBlock);
}
/**
* Get a new InvocationImpl response from the store using a
* MIDlet suiteId and classname.
* The response is removed from the store.
*
* @param invoc an InvocationImpl to fill with the response
* @param suiteId the MIDletSuite ID
* @param classname the classname
* @param shouldBlock true if the method should block
* waiting for an Invocation
*
* @return <code>InvocationImpl</code> if any was found with
* the same MIDlet suiteId and classname if one was requested;
* <code>null</code> is returned if there is no matching Invocation
*/
static InvocationImpl getResponse(InvocationImpl invoc,
int suiteId, String classname,
boolean shouldBlock) {
invoc.suiteId = suiteId;
invoc.classname = classname;
return get(invoc, MODE_RESPONSE, shouldBlock);
}
/**
* Performs cleanup for a ContentHandler
* by suiteId and classname.
* <p>
* Any marked {@link #setCleanup} invocations still in the queue
* are handled based on status:
* <UL>
* <li>ACTIVE Invocations are returned from this method
* so they can be have the ERROR status set and so the
* invoking application relaunched.</li>
* <li>INIT Invocations are requeued to the invoking application
* with ERROR status. </li>
* <li>OK, CANCELLED, ERROR, or INITIATED Invocations are
* discrded.</li>
* <li>HOLD status Invocations are retained pending
* completion of previous Invocation. TBD: Chained HOLDs...</li>
* </ul>
*
* @param suiteId the MIDletSuite ID
* @param classname the classname
*
* @return <code>InvocationImpl</code> if any was found with
* the same MIDlet suiteId and classname;
* <code>null</code> is returned if there is no matching Invocation
*/
static InvocationImpl getCleanup(int suiteId, String classname) {
InvocationImpl invoc = new InvocationImpl();
invoc.suiteId = suiteId;
invoc.classname = classname;
return get(invoc, MODE_CLEANUP, false);
}
/**
* Get an Invocation from the store based on its <code>tid</code>.
* The normal state transitions and dispositions are NOT performed.
* If TID == 0 then the first tid is used as the reference.
* If TID == 0 and relative == 0 then null is returned.
* This method never waits.
*
* @param tid the <code>tid</code> to fetch
* @param relative -1, 0, +1 to get previous, equal, or next
* @return an InvocationImpl object if a matching tid was found;
* otherwise <code>null</code>
*/
static InvocationImpl getByTid(int tid, int relative) {
InvocationImpl invoc = new InvocationImpl();
int mode = MODE_TID;
if (tid != 0) {
if (relative < 0) {
mode = MODE_TID_PREV;
} else if (relative > 0) {
mode = MODE_TID_NEXT;
}
}
invoc.suiteId = MIDletSuite.UNUSED_SUITE_ID;
invoc.classname = null;
invoc.tid = tid;
return get(invoc, mode, false);
}
/**
* Get an InvocationImpl from the store using a MIDlet suiteId
* and classname.
* The mode controls whether getting an Invocation
* from the store removes it from the store.
*
* @param invoc InvocationImpl to fill in with result
* @param mode one of {@link #MODE_REQUEST}, {@link #MODE_RESPONSE},
* or {@link #MODE_CLEANUP}, {@link #MODE_LREQUEST},
* or {@link #MODE_LRESPONSE}, {@link #MODE_TID}.
* @param shouldBlock true if the method should block
* waiting for an Invocation
*
* @return <code>InvocationImpl</code> if any was found with
* the same MIDlet suiteId and classname if one was requested;
* <code>null</code> is returned if there is no matching Invocation
*/
private static InvocationImpl get(InvocationImpl invoc,
int mode, boolean shouldBlock) {
String classname = invoc.classname;
invoc.setArgs(null);
invoc.setData(null);
int s = 0;
int oldCancelCount = cancelCount;
while ((s = get0(invoc, invoc.suiteId, invoc.classname,
mode, shouldBlock)) != 1) {
if (s == -1) {
/*
* Sizes of arguments and data buffers were insufficient
* reallocate and retry.
*/
invoc.setArgs(new String[invoc.argsLen]);
invoc.setData(new byte[invoc.dataLen]);
continue;
}
// Don't wait unless requested
if (!shouldBlock) {
break;
}
// No matching request; retry unless cancelled
if (cancelCount > oldCancelCount) {
// Was cancelled; s == 0 -> no Invocation
break;
}
}
// Update the return if no invocation
if (s == 0) {
invoc = null;
}
if (AppProxy.LOG_INFO) {
AppProxy.getCurrent().logInfo("Store get: " +
classname +
", mode: " + mode +
", " + invoc);
}
return invoc;
}
/**
* Sets the status of an existing Invocation.
* If the status is OK, CANCELLED, ERROR, or INITIATED
* and a response is required then the invocation is
* requeued to the invoking application; if no response
* is required the request is discarded and the transaction id (tid)
* is set to zero.
*
* @param invoc an InvocationImpl previously retrieved with get
*/
static void setStatus(InvocationImpl invoc) {
setStatus0(invoc);
if (AppProxy.LOG_INFO) {
AppProxy.getCurrent().logInfo("Store setStatus0: " + invoc);
}
}
/**
* Updates the parameters of the invocation in the native store.
* The ID, URL, type, action, arguments, and data are
* stored again in native.
*
* @param invoc an InvocationImpl previously retrieved with get
*/
static void setParams(InvocationImpl invoc) {
setParams0(invoc);
if (AppProxy.LOG_INFO) {
AppProxy.getCurrent().logInfo("Store setParams0: " + invoc);
}
}
/**
* Listen for a matching invocation.
* When a matching invocation is present, true is returned.
* Each Invocation instance is only returned once.
* After it has been returned once; it is ignored subsequently.
*
* @param suiteId the MIDlet suiteId to search for,
* MUST not be <code>null</code>
* @param classname to match, must not be null
* @param request true to listen for a request; else a response
* @param shouldBlock true if the method should block
* waiting for an Invocation
*
* @return true if a matching invocation is present; false otherwise
*/
static boolean listen(int suiteId, String classname,
boolean request, boolean shouldBlock) {
if (suiteId == MIDletSuite.UNUSED_SUITE_ID || classname == null) {
throw new NullPointerException();
}
int mode = (request ? MODE_LREQUEST : MODE_LRESPONSE);
boolean pending = false;
int oldCancelCount = cancelCount;
while ((pending = listen0(suiteId, classname,
mode, shouldBlock)) == false &&
shouldBlock) {
// No pending request; retry unless cancelled
if (cancelCount > oldCancelCount) {
// Was cancelled; s == 0 -> no Invocation
break;
}
}
if (AppProxy.LOG_INFO) {
AppProxy.getCurrent().logInfo("Store listen: " + classname +
", request: " + request +
", pending: " + pending);
}
return pending;
}
/**
* Reset the flags for requests or responses that are pending.
* Once reset, any pending requests or responses will be
* returned when listen0 is called.
*
* @param suiteId the MIDlet suiteId to search for,
* MUST not be <code>null</code>
* @param classname to match, must not be null
* @param request true to reset request notification flags;
* else reset response notification flags
*/
static void setListenNotify(int suiteId, String classname,
boolean request) {
if (suiteId == MIDletSuite.UNUSED_SUITE_ID || classname == null) {
throw new NullPointerException();
}
int mode = (request ? MODE_LREQUEST : MODE_LRESPONSE);
setListenNotify0(suiteId, classname, mode);
if (AppProxy.LOG_INFO) {
AppProxy.getCurrent().logInfo("Store setListenNotify: " +
classname +
", request: " + request);
}
}
/**
* Cancel a blocked {@link #get} or {@link #listen}
* method if it is blocked in the native code.
*/
static void cancel() {
cancelCount++;
cancel0();
}
/**
* Marks any existing invocations for the content handler.
* Any marked invocation will be modified by {@link #getCleanup}.
*
* @param suiteId the suite to mark
* @param classname the MIDlet within the suite
* @param cleanup <code>true</code> to mark the Invocation for
* cleanup at exit
*/
static void setCleanup(int suiteId, String classname, boolean cleanup) {
if (AppProxy.LOG_INFO) {
AppProxy.getCurrent().logInfo("Store setCleanup: " + classname +
": " + cleanup);
}
setCleanup0(suiteId, classname, cleanup);
}
/**
* Return the number of invocations in the native queue.
* @return the number of invocations in the native queue
*/
static int size() {
return size0();
}
/**
* Native method to store a new Invocation.
* All of the fields of the InvocationImpl are stored.
* @param invoc the InvocationImpl to store
*/
private static native void put0(InvocationImpl invoc);
/**
* Native method to fill an available InvocationImpl with an
* available stored Invocation with the status (if non-zero),
* the suiteId, classname in the prototype InvocationImpl.
* Any InvocationImpl with a matching status, suite and
* class will be returned.
* Depending on the mode the stored invocation will be removed
* from the store.
* @param invoc the Invocation containing the suiteId and
* classname to fill in with an available invocation.
* @param suiteId the MIDletSuite ID to match
* @param classname the classname to match
* @param mode one of {@link #MODE_REQUEST}, {@link #MODE_RESPONSE},
* or {@link #MODE_CLEANUP}
* @param shouldBlock True if the method should block until an
* Invocation is available
* @return 1 if a matching invocation was found and returned
* in its entirety; zero if there was no matching invocation;
* -1 if the sizes of the arguments or parameter array were wrong
* @see #get
*/
private static native int get0(InvocationImpl invoc,
int suiteId,
String classname,
int mode,
boolean shouldBlock);
/**
* Sets the status of an existing Invocation
* and handles response required behavior.
*
* @param invoc an InvocationImpl previously retrieved with get.
*/
private static native void setStatus0(InvocationImpl invoc);
/**
* Updates the parameters of the invocation in the native store.
* The ID, URL, type, action, arguments, and data are
* stored again in native.
*
* @param invoc an InvocationImpl previously retrieved with get
*/
private static native void setParams0(InvocationImpl invoc);
/**
* Native method to listen for pending invocations with
* matching suite, classname, and status. Cancel() will
* also cause this method to return if blocked.
* Each Invocation will only be returned once to prevent
* multiple notifications.
*
* @param suiteId the MIDletSuite ID to match
* @param classname the classname to match
* @param mode one of {@link #MODE_LREQUEST}, {@link #MODE_LRESPONSE}
* @param shouldBlock true if the method should block until an
* Invocation is available
* @return true if a matching invocation was found; otherwise false.
* @see #get0
*/
private static native boolean listen0(int suiteId, String classname,
int mode, boolean shouldBlock);
/**
* Native method to reset the listen notified state for pending
* invocations with matching suite, classname and status.
* Each Invocation will only be returned once to prevent
* multiple notifications.
*
* @param suiteId the MIDletSuite ID to match
* @param classname the classname to match
* @param mode one of {@link #MODE_LREQUEST}, {@link #MODE_LRESPONSE}
* <code>false</code> to reset the notified state for responses
* @see #listen0
*/
private static native void setListenNotify0(int suiteId, String classname,
int mode);
/**
* Native method to unblock any threads that might be
* waiting for an invocation by way of having called
* {@link #get0}.
*
*/
private static native void cancel0();
/**
* Sets the cleanup flag in matching Invocations.
* Any marked invocation will be modified by {@link #getCleanup}.
*
* @param suiteId the MIDlet suiteId to search for,
* MUST not be <code>null</code>
* @param classname to match, must not be null
* @param cleanup <code>true</code> to mark the Invocation for
* cleanup at exit
*/
private static native void setCleanup0(int suiteId, String classname,
boolean cleanup);
/**
* Return the number of invocations in the native queue.
* @return the number of invocations in the native queue
*/
private static native int size0();
}
|