/*
*
*
* 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.main;
import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels;
import com.sun.cldc.isolate.Isolate;
import java.util.Vector;
/**
* Implements the mechanism to monitor the startup of a MIDlet.
* It keeps track of which MIDlets are being started
* and are not yet in the MIDletProxyList.
* The information is used to avoid starting the same
* MIDlet twice.
*/
class StartMIDletMonitor implements MIDletProxyListListener {
/**
* Reference to the ProxyList.
*/
private static MIDletProxyList midletProxyList;
/**
* Vector of pending start requests.
*/
private static Vector startPending = new Vector();
/**
* The id of the MIDlet suite being started.
*/
private int suiteId;
/**
* The midlet of the MIDlet being started.
*/
private String midlet;
/**
* The IsolateID of the MIDlet being started.
*/
private Isolate isolate;
/**
* Initializes StartMIDletMonitor class.
* Shall only be called from AmsUtil.
* No need in security checks since it is package private method.
*
* @param theMIDletProxyList MIDletController's container
*/
static void initClass(MIDletProxyList theMIDletProxyList) {
midletProxyList = theMIDletProxyList;
}
/**
* Construct a new StartMIDletMonitor instance to track the
* process of starting a MIDlet in a new Isolate.
* The new instance is appended to the startPending vector
* and is registered with the MIDletProxyList to receive
* notifications if/when the MIDlet starts/fails to start.
*
* @param id ID of an installed suite
* @param midletClassName class name of MIDlet to invoke
*/
private StartMIDletMonitor(int id, String midletClassName) {
suiteId = id;
midlet = midletClassName;
startPending.addElement(this);
midletProxyList.addListener(this);
}
/**
* Sets the Isolate associated with this starting MIDlet.
* It is used to cleanup the Isolate if the start does not
* start correctly.
*
* @param newIsolate the Isolate used to start the MIDlet
*/
void setIsolate(Isolate newIsolate) {
isolate = newIsolate;
}
/**
* Check if the MIDlet is already in the ProxyList or is
* already being started. If so, return.
* If not, start it. Register with the proxy list and
* cleanup when the start of the MIDlet
* succeeds (and is now in the ProxyList) or
* fails (and is eligible to be started again).
*
* @param id ID of an installed suite
* @param midlet class name of MIDlet to invoke; may be null
* @return the new StartMIDletMonitor to allow the MIDlet to be started;
* null if the MIDlet is already active or being started
*/
static StartMIDletMonitor okToStart(int id, String midlet) {
synchronized (startPending) {
// Verify that the requested MIDlet is not already running
// (is not in the MIDletProxyList)
if (midletProxyList.isMidletInList(id, midlet)) {
if (Logging.REPORT_LEVEL <= Logging.WARNING) {
Logging.report(Logging.WARNING, LogChannels.LC_CORE,
"MIDlet already running; execute ignored");
}
return null;
}
/*
* Find the StartMIDletMonitor instance
* to track the startup, (if any)
*/
StartMIDletMonitor start = findMonitor(id, midlet);
if (start == null) {
// Not already starting; register new start
start = new StartMIDletMonitor(id, midlet);
} else {
// MIDlet is already started; return null
start = null;
if (Logging.REPORT_LEVEL <= Logging.WARNING) {
Logging.report(Logging.WARNING, LogChannels.LC_CORE,
"MIDlet already started; execute ignored");
}
}
return start;
}
}
/**
* Scan the startPending list for a matching id and MIDlet.
* The caller must synchronize using {@link #startPending}.
* If <code>midlet</code> is null then it only checks to see
* if the suite is started and returns any monitor for the suite.
* To prevent using stale Isolate state; check that the Isolate (if any)
* has not terminated.
*
* @param id ID of an installed suite
* @param midlet class name of MIDlet to invoke
* @return a StartMIDletMonitor entry with id and midlet;
* otherwise <code>null</code>
*/
private static StartMIDletMonitor findMonitor(int id, String midlet) {
for (int i = 0; i < startPending.size(); i++) {
StartMIDletMonitor pending =
(StartMIDletMonitor)startPending.elementAt(i);
// If there is a terminated Isolate in the list, clean it up
if (pending.isolate != null &&
pending.isolate.isTerminated()) {
// Isolate is not alive, clean the pending entry
startPending.removeElementAt(i);
midletProxyList.removeListener(pending);
// Recheck the element at the same index
i--;
continue; // keep looking
}
if (id == pending.suiteId &&
(midlet == null || midlet.equals(pending.midlet))) {
return pending;
}
}
return null;
}
/**
* Cleanup the matching entry in the startPending list.
* Once removed; the MIDlet will be eligible to be started
* again.
* @param id ID of an installed suite of the notifying MIDlet
* @param midlet class name of MIDlet of the notifying MIDlet
*/
private void cleanupPending(int id, String midlet) {
synchronized (startPending) {
// If the notification is for this monitor
if (id == suiteId &&
(midlet == null || midlet.equals(midlet))) {
// Remove from the startPending list
startPending.removeElement(this);
// Remove the instance as a listener of the MIDletProxyList
midletProxyList.removeListener(this);
}
}
}
/**
* Called when a MIDlet is added to the list.
* If there's a match in the startPending list clean it up.
*
* @param midlet The proxy of the MIDlet being added
*/
public void midletAdded(MIDletProxy midlet) {
IsolateMonitor.addIsolate(midlet, isolate);
cleanupPending(midlet.getSuiteId(), midlet.getClassName());
}
/**
* Called when the state of a MIDlet in the list is updated.
* If there's a match in the startPending list clean it up.
*
* @param midlet The proxy of the MIDlet that was updated
* @param fieldId code for which field of the proxy was updated
*/
public void midletUpdated(MIDletProxy midlet, int fieldId) {
}
/**
* Called when a MIDlet is removed from the list.
* If there's a match in the startPending list clean it up.
*
* @param midlet The proxy of the removed MIDlet
*/
public void midletRemoved(MIDletProxy midlet) {
cleanupPending(midlet.getSuiteId(), midlet.getClassName());
}
/**
* Called when error occurred while starting a MIDlet object.
* If there's a match in the startPending list clean it up.
*
* @param externalAppId ID assigned by the external application manager
* @param suiteId Suite ID of the MIDlet
* @param className Class name of the MIDlet
* @param errorCode start error code
* @param errorDetails start error details
*/
public void midletStartError(int externalAppId, int suiteId,
String className, int errorCode,
String errorDetails) {
cleanupPending(suiteId, className);
}
}
|