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.automation;
028:
029: import java.util.*;
030: import com.sun.midp.main.*;
031: import com.sun.midp.events.*;
032: import com.sun.midp.configurator.Constants;
033: import com.sun.cldc.isolate.*;
034:
035: /**
036: * Class controlling states of the MIDlets
037: */
038: final class AutoMIDletStateController implements
039: MIDletProxyListListener {
040:
041: /** MIDlet proxy list reference. */
042: private MIDletProxyList midletProxyList;
043:
044: /** List of AutoMIDletInfo for MIDlet's we are interested in */
045: private AutoMIDletInfoList midletsInfo;
046:
047: /** The one and only AutoMIDletStateController instance */
048: private static AutoMIDletStateController stateController = null;
049:
050: /**
051: * Private constructor to prevent direct creation of instances.
052: */
053: private AutoMIDletStateController() {
054: midletProxyList = MIDletProxyList.getMIDletProxyList();
055: midletProxyList.addListener(this );
056:
057: midletsInfo = AutoMIDletInfoList.getMIDletInfoList();
058: }
059:
060: /**
061: * Starts MIDlet.
062: *
063: * @param midletDescriptor descriptor of MIDlet to start
064: * @param args MIDlet's arguments
065: * @throws throws RuntimeException if MIDlet couldn't be started
066: * @return AutoMIDletImpl instance representing started MIDlet or
067: * null if there was a problem starting MIDlet
068: */
069: AutoMIDletImpl startMIDlet(
070: AutoMIDletDescriptorImpl midletDescriptor, String[] args) {
071:
072: AutoMIDletImpl midlet = null;
073:
074: synchronized (midletsInfo) {
075: AutoSuiteDescriptorImpl suite;
076: suite = (AutoSuiteDescriptorImpl) midletDescriptor
077: .getSuiteDescriptor();
078:
079: // what is needed to start MIDlet
080: int suiteID = suite.getSuiteID();
081: String midletClassName = midletDescriptor
082: .getMIDletClassName();
083: String midletName = midletDescriptor.getMIDletName();
084:
085: // MIDlet's arguments: only 3 are supported by now
086: String arg1 = null;
087: String arg2 = null;
088: String arg3 = null;
089: if (args != null) {
090: if (args.length > 0) {
091: arg1 = args[0];
092: }
093:
094: if (args.length > 1) {
095: arg2 = args[1];
096: }
097:
098: if (args.length > 2) {
099: arg3 = args[2];
100: }
101: }
102:
103: // create info for MIDlet to be started
104: AutoMIDletInfo info = midletsInfo.addToList(suiteID,
105: midletClassName);
106:
107: // initiate MIDlet starting
108: // IMPL_NOTE: add passing of memory quotas and VM profile name
109: Isolate isolate = AmsUtil.startMidletInNewIsolate(suiteID,
110: midletClassName, midletName, arg1, arg2, arg3);
111:
112: // wait for MIDlet to start
113: while (info.midletProxy == null && info.startError == false) {
114: try {
115: midletsInfo.wait();
116: } catch (InterruptedException e) {
117: // just ignore
118: }
119: }
120:
121: // By now, either MIDlet is running,
122: // either there was a problem starting it
123: if (info.startError) {
124: midletsInfo.removeFromList(info);
125:
126: String errorMsg = "";
127: switch (info.startErrorCode) {
128: case Constants.MIDLET_ISOLATE_CONSTRUCTOR_FAILED:
129: errorMsg = "MIDlet Isolate constructor failed";
130: break;
131:
132: case Constants.MIDLET_RESOURCE_LIMIT:
133: errorMsg = "Not enough resources to start the MIDlet";
134: break;
135:
136: case Constants.MIDLET_ISOLATE_RESOURCE_LIMIT:
137: errorMsg = "Maximum Isolate count is exceeded";
138: break;
139:
140: case Constants.MIDLET_OUT_OF_MEM_ERROR:
141: errorMsg = "Not enough memory to start the MIDlet";
142: break;
143:
144: case Constants.MIDLET_SUITE_NOT_FOUND:
145: errorMsg = "MIDlet suite is not found";
146: break;
147:
148: case Constants.MIDLET_SUITE_DISABLED:
149: errorMsg = "MIDlet suite is disabled";
150: break;
151:
152: case Constants.MIDLET_CLASS_NOT_FOUND:
153: errorMsg = "MIDlet class is not found";
154: break;
155:
156: case Constants.MIDLET_INSTANTIATION_EXCEPTION:
157: errorMsg = "MIDlet instantiation exception";
158: break;
159:
160: case Constants.MIDLET_ILLEGAL_ACCESS_EXCEPTION:
161: errorMsg = "MIDlet illegal access exception";
162: break;
163:
164: case Constants.MIDLET_CONSTRUCTOR_FAILED:
165: errorMsg = "MIDlet constructor failed";
166: break;
167:
168: default:
169: errorMsg = "Error starting MIDlet: "
170: + info.startErrorCode;
171: break;
172:
173: }
174:
175: String details = info.startErrorDetails;
176: if (details != null) {
177: errorMsg += " (" + details + ")";
178: }
179:
180: throw new RuntimeException(errorMsg);
181: } else {
182: midlet = new AutoMIDletImpl(midletDescriptor);
183: info.midlet = midlet;
184: info.midletIsolate = isolate;
185: }
186: }
187:
188: return midlet;
189: }
190:
191: /**
192: * Initiates switching MIDlet to specified state.
193: *
194: * @param midlet AutoMIDletImpl instance representing MIDlet to switch
195: * @param state state to switch to
196: */
197: void switchTo(AutoMIDletImpl midlet, AutoMIDletLifeCycleState state) {
198: MIDletProxy midletProxy = midletsInfo.findMIDletProxy(midlet);
199:
200: if (midletProxy != null) {
201: if (state == AutoMIDletLifeCycleState.ACTIVE) {
202: midletProxy.activateMidlet();
203: } else if (state == AutoMIDletLifeCycleState.PAUSED) {
204: midletProxy.pauseMidlet();
205: } else if (state == AutoMIDletLifeCycleState.DESTROYED) {
206: midletProxy.destroyMidlet();
207: }
208: }
209: }
210:
211: /**
212: * Gets AutoMIDletStateController instance.
213: *
214: * @return AutoMIDletStateController instance
215: */
216: synchronized static AutoMIDletStateController getMIDletStateController() {
217: if (stateController == null) {
218: stateController = new AutoMIDletStateController();
219: }
220:
221: return stateController;
222: }
223:
224: /**
225: * Called when a MIDlet Isolate has exited.
226: *
227: * @param midletProxy The proxy of the MIDlet being added
228: */
229: void isolateExited(MIDletProxy midletProxy) {
230: synchronized (midletsInfo) {
231: AutoMIDletInfo info = midletsInfo
232: .findMIDletInfo(midletProxy);
233: if (info != null) {
234: AutoMIDletImpl midlet = info.midlet;
235: if (midlet != null) {
236: midlet
237: .stateChanged(AutoMIDletLifeCycleState.DESTROYED);
238: }
239:
240: // remove MIDlet info from our list as well
241: midletsInfo.removeFromList(info);
242: }
243: }
244: }
245:
246: /**
247: * Start notifier thread that will wait for the MIDlet's isolate to exit.
248: *
249: * @param proxy proxy of MIDlet
250: */
251: private void startNotificationThread(AutoMIDletInfo info) {
252: IsolateExitNotifier notifier = new IsolateExitNotifier();
253:
254: notifier.midletProxy = info.midletProxy;
255: notifier.midletIsolate = info.midletIsolate;
256: notifier.parent = this ;
257:
258: new Thread(notifier).start();
259: }
260:
261: /**
262: * MIDletProxyListListener interface implementation
263: */
264:
265: /**
266: * Called when a MIDlet is added to the list.
267: *
268: * @param midletProxy The proxy of the MIDlet being added
269: */
270: public void midletAdded(MIDletProxy midletProxy) {
271: synchronized (midletsInfo) {
272: AutoMIDletInfo info = midletsInfo
273: .findMIDletInfo(midletProxy);
274: if (info != null) {
275: info.midletProxy = midletProxy;
276:
277: // notify waiter that MIDlet has been created
278: midletsInfo.notify();
279: }
280: }
281: }
282:
283: /**
284: * Called when the state of a MIDlet in the list is updated.
285: *
286: * @param midletProxy The proxy of the MIDlet that was updated
287: * @param fieldID code for which field of the proxy was updated
288: */
289: public void midletUpdated(MIDletProxy midletProxy, int fieldID) {
290: AutoMIDletImpl midlet = midletsInfo.findMIDlet(midletProxy);
291:
292: // notify AutoMIDletImpl about state change
293: if (midlet != null
294: && fieldID == MIDletProxyListListener.MIDLET_STATE) {
295:
296: switch (midletProxy.getMidletState()) {
297: case MIDletProxy.MIDLET_ACTIVE:
298: midlet.stateChanged(AutoMIDletLifeCycleState.ACTIVE);
299: break;
300:
301: case MIDletProxy.MIDLET_PAUSED:
302: midlet.stateChanged(AutoMIDletLifeCycleState.PAUSED);
303: break;
304:
305: default:
306: break;
307: }
308: }
309: }
310:
311: /**
312: * Called when a MIDlet is removed from the list.
313: *
314: * @param midletProxy The proxy of the removed MIDlet
315: */
316: public void midletRemoved(MIDletProxy midletProxy) {
317: synchronized (midletsInfo) {
318: AutoMIDletInfo info = midletsInfo
319: .findMIDletInfo(midletProxy);
320: if (info != null) {
321: startNotificationThread(info);
322: }
323: }
324: }
325:
326: /**
327: * Called when error occurred while starting a MIDlet object.
328: *
329: * @param externalAppID ID assigned by the external application manager
330: * @param suiteID Suite ID of the MIDlet
331: * @param className Class name of the MIDlet
332: * @param errorCode start error code
333: * @param errorDetails start error details
334: */
335: public void midletStartError(int externalAppID, int suiteID,
336: String className, int errorCode, String errorDetails) {
337:
338: synchronized (midletsInfo) {
339: AutoMIDletInfo info = midletsInfo.findMIDletInfo(suiteID,
340: className);
341:
342: if (info != null) {
343: // set error flag and notify waiter
344: info.startError = true;
345: info.startErrorCode = errorCode;
346: info.startErrorDetails = errorDetails;
347: midletsInfo.notify();
348: }
349: }
350: }
351:
352: }
353:
354: /**
355: * Waits for an isolate to terminate and then notifies the native app
356: * manager.
357: */
358: final class IsolateExitNotifier implements Runnable {
359: /** MIDlet information. */
360: MIDletProxy midletProxy;
361:
362: /** Isolate of the MIDlet. */
363: Isolate midletIsolate;
364:
365: /** Parent to notify */
366: AutoMIDletStateController parent;
367:
368: /** Performs this classes function. */
369: public void run() {
370: midletIsolate.waitForExit();
371: parent.isolateExited(midletProxy);
372: }
373: }
|