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.main;
028:
029: import com.sun.midp.security.SecurityToken;
030: import com.sun.midp.io.j2me.serversocket.Socket;
031: import com.sun.midp.midlet.MIDletSuite;
032:
033: import com.sun.midp.events.EventTypes;
034: import com.sun.midp.events.EventQueue;
035: import com.sun.midp.events.EventListener;
036: import com.sun.midp.events.Event;
037: import com.sun.midp.events.NativeEvent;
038:
039: import java.io.DataInputStream;
040: import java.io.IOException;
041: import java.io.PrintStream;
042: import java.util.Vector;
043:
044: import javax.microedition.io.ServerSocketConnection;
045: import javax.microedition.io.SocketConnection;
046:
047: /**
048: * A service for testing the Native AMS (nams). Listens on a socket and
049: * processes commands by calling native methods that invoke the native AMS
050: * API. Receives Native AMS callbacks and sends the information out through
051: * the socket.
052: */
053: public class NamsTestService implements EventListener, Runnable {
054:
055: static final int PORT = 13322;
056: static final String PFX = "namstestsvc: ";
057:
058: /**
059: * Prints string s to stdout, prefixed by PFX.
060: */
061: static void d(String s) {
062: System.out.print(PFX + s + "\n");
063: }
064:
065: /**
066: * Initializes the nams service. Sets up translation of native callbacks
067: * into events, opens up a socket, and creates the service.
068: */
069: public static void init(SecurityToken token, EventQueue eq) {
070: ServerSocketConnection serv;
071:
072: initialize(MIDletSuiteUtils.getIsolateId());
073:
074: try {
075: Socket s = new Socket();
076: s.open(PORT, token);
077: serv = (ServerSocketConnection) s;
078: } catch (Throwable t) {
079: d("failed " + t.toString());
080: t.printStackTrace();
081: return;
082: }
083:
084: new NamsTestService(serv, eq);
085: }
086:
087: ServerSocketConnection serv;
088: DataInputStream in;
089: PrintStream out;
090: boolean reading;
091:
092: /**
093: * Constructor for the test service. Starts the listener thread and
094: * registers itself as a listener for test events.
095: */
096: NamsTestService(ServerSocketConnection s, EventQueue eq) {
097: serv = s;
098: new Thread(this ).start();
099:
100: eq.registerEventListener(EventTypes.TEST_EVENT, this );
101: }
102:
103: /**
104: * The socket listener loop. Awaits a connection, processes requests from
105: * the connection, then goes back to waiting, forever.
106: */
107: public void run() {
108: SocketConnection sock;
109:
110: while (true) {
111: d("listening on port " + PORT);
112: try {
113: sock = (SocketConnection) serv.acceptAndOpen();
114: } catch (IOException ioe) {
115: d("accept failed: " + ioe);
116: try {
117: serv.close();
118: } catch (IOException ignored) {
119: }
120: return;
121: }
122:
123: d("connected");
124: readSocket(sock);
125: d("disconnected");
126: }
127: }
128:
129: /**
130: * Opens input and output streams from a fresh connection, then processes
131: * input from the socket. Closes and cleans up after input processing
132: * completes.
133: */
134: void readSocket(SocketConnection sock) {
135: try {
136: in = sock.openDataInputStream();
137: } catch (IOException ioe) {
138: d("input stream failed: " + ioe);
139: try {
140: sock.close();
141: } catch (IOException ignored) {
142: }
143: return;
144: }
145:
146: try {
147: out = new PrintStream(sock.openDataOutputStream());
148: } catch (IOException ioe) {
149: d("output stream failed: " + ioe);
150:
151: try {
152: in.close();
153: } catch (IOException ignored) {
154: }
155: in = null;
156:
157: try {
158: sock.close();
159: } catch (IOException ignored) {
160: }
161:
162: return;
163: }
164:
165: out.println("> NAMS Test Service ready.");
166: readLines();
167: out.close();
168: out = null;
169:
170: try {
171: in.close();
172: } catch (IOException ignored) {
173: }
174: in = null;
175:
176: try {
177: sock.close();
178: } catch (IOException ignored) {
179: }
180: }
181:
182: /**
183: * Reads lines of input from the socket and processes them. Reads as long
184: * as the 'reading' boolean is true, then returns.
185: */
186: void readLines() {
187: StringBuffer sb = new StringBuffer(100);
188:
189: reading = true;
190: while (reading) {
191: int b;
192:
193: try {
194: b = in.read();
195: } catch (IOException ioe) {
196: break;
197: }
198:
199: if (b == -1 && sb.length() == 0) {
200: break;
201: }
202:
203: if (b == -1 || b == '\n') {
204: int len = sb.length();
205: if (len > 0 && sb.charAt(len - 1) == '\r') {
206: sb.setLength(len - 1);
207: }
208: processLine(tokenize(sb.toString()));
209: sb.setLength(0);
210: } else {
211: sb.append((char) (b & 0xff));
212: }
213: }
214: }
215:
216: /**
217: * Tokenizes a string in a simple fashion. Given a line of input, returns
218: * an array of strings containing tokens. A token consists of a sequence
219: * of nonblank characters. Tokens are separated by one or more blanks.
220: */
221: String[] tokenize(String st) {
222: Vector vec = new Vector();
223: int start = -1;
224: int cur = 0;
225: int len = st.length();
226:
227: while (true) {
228: while (cur < len && st.charAt(cur) == ' ') {
229: cur += 1;
230: }
231:
232: start = cur;
233:
234: while (cur < len && st.charAt(cur) != ' ') {
235: cur += 1;
236: }
237:
238: if (start >= len) {
239: break;
240: }
241:
242: vec.addElement(st.substring(start, cur));
243: }
244:
245: String[] arr = new String[vec.size()];
246: vec.copyInto(arr);
247: return arr;
248: }
249:
250: /**
251: * A simple argument checking function. Given an argument array argv,
252: * ensures that it is exactly rqd elements long, and then attempts to
253: * parse argument idx as an integer. If all of these are successful, the
254: * parsed integer value is returned in an Integer object. Otherwise, null
255: * is returned.
256: */
257: Integer check(String[] argv, int rqd, int idx) {
258: if (argv.length != rqd) {
259: reply("> ?");
260: return null;
261: }
262:
263: try {
264: return Integer.valueOf(argv[idx]);
265: } catch (NumberFormatException nfe) {
266: reply("> ?");
267: return null;
268: }
269: }
270:
271: void echo(String[] argv, String pfx, int argStart) {
272: String s = pfx + argv[argStart];
273: for (int i = argStart + 1; i < argv.length; i++) {
274: s += " " + argv[i];
275: }
276: reply(s);
277: }
278:
279: void reply(String s) {
280: d(s);
281:
282: // IMPL_NOTE not thread-safe
283: // IMPL_NOTE if no connection, should buffer up events
284: // and send them all out when a connection arrives
285:
286: if (out != null) {
287: out.println(s);
288: }
289: }
290:
291: /**
292: * Processes a command and its arguments.
293: */
294: void processLine(String[] sa) {
295: if (sa.length == 0) {
296: return;
297: }
298:
299: echo(sa, "< ", 0);
300:
301: if ("quit".equals(sa[0])) {
302: reading = false;
303: } else if ("echo".equals(sa[0])) {
304: // no need to do anything
305: // if (sa.length > 1) {
306: // echo(sa, "> ", 1);
307: // }
308: } else if ("createstart".equals(sa[0])) {
309: Integer app = check(sa, 4, 3);
310: if (app != null) {
311: int suiteId = MIDletSuite.UNUSED_SUITE_ID;
312: try {
313: suiteId = Integer.parseInt(sa[1]);
314: } catch (NumberFormatException nfe) {
315: // Intentionally ignored
316: }
317: NamsAPIWrapper.midletCreateStart(suiteId, sa[2], app
318: .intValue());
319: }
320: } else if ("resume".equals(sa[0])) {
321: Integer app = check(sa, 2, 1);
322: if (app != null) {
323: NamsAPIWrapper.midletResume(app.intValue());
324: }
325: } else if ("pause".equals(sa[0])) {
326: Integer app = check(sa, 2, 1);
327: if (app != null) {
328: NamsAPIWrapper.midletPause(app.intValue());
329: }
330: } else if ("destroy".equals(sa[0])) {
331: Integer app = check(sa, 3, 1);
332: if (app != null) {
333: int timeout = -1;
334: try {
335: timeout = Integer.parseInt(sa[2]);
336: } catch (NumberFormatException nfe) {
337: // Intentionally ignored
338: }
339: NamsAPIWrapper.midletDestroy(app.intValue(), timeout);
340: }
341: } else if ("setfg".equals(sa[0])) {
342: Integer app = check(sa, 2, 1);
343: if (app != null) {
344: NamsAPIWrapper.midletSetForeground(app.intValue());
345: }
346: } else if ("stop".equals(sa[0])) {
347: NamsAPIWrapper.midpSystemStop();
348: } else if ("help".equals(sa[0])) {
349: for (int ii = 0; ii < helpmsg.length; ii++) {
350: reply("> " + helpmsg[ii]);
351: }
352: } else {
353: reply("> ?");
354: }
355: }
356:
357: /**
358: * Help message strings.
359: */
360: String helpmsg[] = { "createstart suite-name class-name app-id",
361: "destroy app-id timeout", "echo [args ...]",
362: "pause app-id", "quit", "resume app-id", "setfg app-id",
363: "stop" };
364:
365: // -------------------- interface EventListener --------------------
366:
367: /**
368: * Preprocesses events. Does no preprocessing, so always returns true.
369: */
370: public boolean preprocess(Event event, Event waitingEvent) {
371: return true;
372: }
373:
374: /**
375: * Processes test events. Decodes the event and sends the information out
376: * through the socket.
377: */
378: public void process(Event event) {
379: NativeEvent e = (NativeEvent) event;
380: int appId = e.intParam1;
381: int callbackId = e.intParam2;
382: String state = getStateByValue(e.intParam3);
383: int reason = e.intParam4;
384: String s;
385:
386: switch (callbackId) {
387: case 0: // background
388: reply("> background, appId = " + appId + ", state = "
389: + state + ", reason = " + reason);
390: break;
391: case 1: // foreground
392: reply("> foreground, appId = " + appId + ", state = "
393: + state + ", reason = " + reason);
394: case 2: // state change
395: reply("> state, appId = " + appId + ", state = " + state
396: + ", reason = " + reason);
397: break;
398: default:
399: reply("> callbackId=" + callbackId + " appId=" + appId
400: + " state=" + state + " reason=" + reason);
401: break;
402: }
403: }
404:
405: /**
406: * Converts a midlet state (including foreground/background states) value
407: * into readable string.
408: *
409: * @param state state value to convert
410: *
411: * @return a string representing the given state value
412: */
413: private String getStateByValue(int state) {
414: /* IMPL_NOTE: see midpNativeAppManager.h for the definitions */
415: final String[] stateStrings = { "MIDP_MIDLET_STATE_ACTIVE",
416: "MIDP_MIDLET_STATE_PAUSED",
417: "MIDP_MIDLET_STATE_DESTROYED",
418: "MIDP_MIDLET_STATE_ERROR",
419: "MIDP_DISPLAY_STATE_FOREGROUND",
420: "MIDP_DISPLAY_STATE_BACKGROUND",
421: "MIDP_DISPLAY_STATE_FOREGROUND_REQUEST",
422: "MIDP_DISPLAY_STATE_BACKGROUND_REQUEST" };
423:
424: if (state >= 1 && state < stateStrings.length) {
425: return stateStrings[state - 1];
426: }
427:
428: return "unknown (" + state + ")";
429: }
430:
431: // -------------------- natives --------------------
432:
433: /**
434: * Initializes the native portion of the NAMS Test Service.
435: *
436: * @param isolateId the isolateId to which test events are sent
437: */
438: private native static void initialize(int isolateId);
439:
440: /**
441: * Cleans up the native portion of the NAMS Test Service.
442: */
443: private native static void cleanup();
444: }
|