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.installer;
028:
029: import java.io.*;
030:
031: import java.util.*;
032:
033: import com.sun.cldc.isolate.*;
034:
035: import com.sun.midp.main.AmsUtil;
036:
037: import javax.microedition.io.*;
038:
039: import javax.microedition.lcdui.*;
040:
041: import javax.microedition.midlet.*;
042:
043: import javax.microedition.rms.*;
044:
045: import com.sun.midp.io.j2me.storage.*;
046:
047: import com.sun.midp.i18n.Resource;
048: import com.sun.midp.i18n.ResourceConstants;
049:
050: import com.sun.midp.midlet.*;
051:
052: import com.sun.midp.midletsuite.*;
053:
054: import com.sun.midp.security.*;
055: import com.sun.midp.configurator.Constants;
056:
057: import com.sun.midp.log.Logging;
058: import com.sun.midp.log.LogChannels;
059:
060: /**
061: * Fetches a list of URLs pointing to test suites to install, then,
062: * for each specified test suite in parallel, installs/updates the suite,
063: * runs the first MIDlet in the suite in a loop specified number of iterations
064: * or until the new version of the suite is not found, then removes the suite.
065: * <p>
066: * The MIDlet uses these application properties as arguments: </p>
067: * <ol>
068: * <li>arg-0: URL for html page with links to test suites. This html page
069: * looks like this:
070: * <a href="http://localhost/suite1.jad">Suite1</a>
071: * <a href="http://localhost/suite2.jad">Suite2</a>
072: * <li>arg-1: Used to override the default domain used when installing
073: * an unsigned suite. The default is maximum to allow the runtime API tests
074: * be performed automatically without tester interaction. The domain name
075: * may be followed by a colon and a list of permissions that must be allowed
076: * even if they are not listed in the MIDlet-Permissions attribute in the
077: * application descriptor file. Instead of the list a keyword "all" can be
078: * specified indicating that all permissions must be allowed, for example:
079: * operator:all.
080: * <li>arg-2: Integer number, specifying how many iterations to run
081: * the suite. If argument is not given or less then zero, then suite
082: * will be run until the new version of the suite is not found.
083: * </ol>
084: * <p>
085: * If arg-0 is not given then a form will be used to query the tester for
086: * the arguments.</p>
087: */
088: public class AutoTesterMulti extends AutoTesterBase implements
089: AutoTesterInterface {
090: /**
091: * Info about suites to install
092: */
093: Vector installList = new Vector();
094:
095: /**
096: * Create and initialize a new auto tester MIDlet.
097: */
098: public AutoTesterMulti() {
099: super ();
100:
101: if (url != null) {
102: startBackgroundTester();
103: } else if (restoreSession()) {
104: // continuation of a previous session
105: startBackgroundTester();
106: } else {
107: /**
108: * No URL has been provided, ask the user.
109: *
110: * commandAction will subsequently call startBackgroundTester.
111: */
112: getUrl();
113: }
114: }
115:
116: /** Run the installer. */
117: public void run() {
118: installAndPerformTests(midletSuiteStorage, installer, url);
119: }
120:
121: /**
122: * Restore the data from the last session, since this version of the
123: * autotester does not have sessions it just returns false.
124: *
125: * @return true if there was data saved from the last session
126: */
127: public boolean restoreSession() {
128: return false;
129: }
130:
131: /**
132: * Get list of test suites, for each suite start a thread
133: * that performs testing, wait until all threads have finished.
134: *
135: * @param inp_storage MIDletSuiteStorage object
136: * @param inp_installer Installer object
137: * @param inp_url URL for the html page with links to suites
138: */
139: public void installAndPerformTests(MIDletSuiteStorage inp_storage,
140: Installer inp_installer, String inp_url) {
141:
142: fetchInstallList(url);
143: int totalSuites = installList.size();
144: if (totalSuites > 0) {
145: Thread[] threads = new Thread[totalSuites];
146:
147: // create threads
148: for (int i = 0; i < totalSuites; i++) {
149: SuiteDownloadInfo suite = (SuiteDownloadInfo) installList
150: .elementAt(i);
151: threads[i] = new Thread(new AutoTesterRunner(suite.url,
152: inp_storage, inp_installer));
153: }
154:
155: // start threads
156: for (int i = 0; i < totalSuites; i++) {
157: threads[i].start();
158: }
159:
160: // wait for threads to finish
161: for (int i = 0; i < totalSuites; i++) {
162: try {
163: threads[i].join();
164: } catch (Exception ex) {
165: // just ignore
166: }
167: }
168:
169: notifyDestroyed();
170: return;
171: }
172: }
173:
174: /**
175: * Go to given URL, fetch and parse html page with links
176: * to tests suites. If there was error while fetching
177: * or parsing, display an alert.
178: *
179: * @param url URL for html page with links to suites
180: */
181: private void fetchInstallList(String url) {
182: StreamConnection conn = null;
183: InputStreamReader in = null;
184: String errorMessage;
185:
186: try {
187: conn = (StreamConnection) Connector.open(url,
188: Connector.READ);
189: in = new InputStreamReader(conn.openInputStream());
190: try {
191: installList = SuiteDownloadInfo
192: .getDownloadInfoFromPage(in);
193: if (installList.size() > 0) {
194: return;
195: }
196: errorMessage = Resource
197: .getString(ResourceConstants.AMS_DISC_APP_CHECK_URL_MSG);
198: } catch (IllegalArgumentException ex) {
199: errorMessage = Resource
200: .getString(ResourceConstants.AMS_DISC_APP_URL_FORMAT_MSG);
201: } catch (Exception ex) {
202: errorMessage = ex.getMessage();
203: }
204: } catch (Exception ex) {
205: errorMessage = Resource
206: .getString(ResourceConstants.AMS_DISC_APP_CONN_FAILED_MSG);
207: } finally {
208: try {
209: conn.close();
210: in.close();
211: } catch (Exception e) {
212: if (Logging.REPORT_LEVEL <= Logging.WARNING) {
213: Logging.report(Logging.WARNING, LogChannels.LC_AMS,
214: "close threw an Exception");
215: }
216: }
217: }
218:
219: Alert a = new Alert(
220: Resource.getString(ResourceConstants.ERROR),
221: errorMessage, null, AlertType.ERROR);
222: a.setTimeout(Alert.FOREVER);
223: display.setCurrent(a);
224: }
225:
226: /**
227: * An runnable class that runs specified test suite in a loop.
228: */
229: private class AutoTesterRunner implements Runnable {
230: /**
231: * Number of retries to fetch a suite from given URL before exiting
232: */
233: private final static int MAX_RETRIES = 20;
234:
235: /**
236: * Time to wait before trying to fetch a suite again
237: */
238: private final static int RETRY_WAIT_TIME = 30000;
239:
240: /**
241: * URL for the test suite
242: */
243: private String url;
244:
245: /**
246: * MIDletSuiteStorage object
247: */
248: MIDletSuiteStorage storage;
249:
250: /**
251: * Installer object
252: */
253: Installer installer;
254:
255: /**
256: * Constructor.
257: *
258: * @param theUrl URL for the test suite
259: * @param theStorage MIDletSuiteStorage object to use
260: * @param theInstaller Installer object to use
261: */
262: private AutoTesterRunner(String theUrl,
263: MIDletSuiteStorage theStorage, Installer theInstaller) {
264: url = theUrl;
265: storage = theStorage;
266: installer = theInstaller;
267: }
268:
269: /**
270: * In a loop, install/update the suite and run it until
271: * new version of the suite is not found.
272: */
273: public void run() {
274: // when installing suite for the first time,
275: // do not retry because URL may be wrong and
276: // we want immediately tell user about it.
277: boolean retry = false;
278: boolean hasSuite = true;
279:
280: int suiteId = MIDletSuite.UNUSED_SUITE_ID;
281: int lastSuiteId = MIDletSuite.UNUSED_SUITE_ID;
282:
283: MIDletInfo midletInfo;
284: Isolate testIsolate;
285:
286: for (; loopCount != 0 && hasSuite;) {
287: suiteId = installSuite(retry);
288: if (suiteId != MIDletSuite.UNUSED_SUITE_ID) {
289: midletInfo = getFirstMIDletOfSuite(suiteId, storage);
290: testIsolate = AmsUtil.startMidletInNewIsolate(
291: suiteId, midletInfo.classname,
292: midletInfo.name, null, null, null);
293:
294: testIsolate.waitForExit();
295:
296: if (loopCount > 0) {
297: loopCount -= 1;
298: }
299:
300: lastSuiteId = suiteId;
301:
302: // suite has been found, so URL isn't wrong.
303: // next time if there is no suite, do retries.
304: retry = true;
305: } else {
306: hasSuite = false;
307: }
308: }
309:
310: if (midletSuiteStorage != null
311: && lastSuiteId != MIDletSuite.UNUSED_SUITE_ID) {
312: try {
313: midletSuiteStorage.remove(lastSuiteId);
314: } catch (Throwable ex) {
315: // ignore
316: }
317: }
318: }
319:
320: /**
321: * Install the suite.
322: *
323: * @param retry if true, do a number of retries to
324: * install a suite in case it hasn't been found.
325: *
326: * @return suiteId if the suite has been installed
327: */
328: private int installSuite(boolean retry) {
329: int maxRetries = retry ? MAX_RETRIES : 1;
330: int retryCount = 0;
331: int suiteId = MIDletSuite.UNUSED_SUITE_ID;
332:
333: for (; retryCount < maxRetries; ++retryCount) {
334: try {
335: synchronized (installer) {
336: // force an overwrite and remove the RMS data
337: suiteId = installer.installJad(url,
338: Constants.INTERNAL_STORAGE_ID, true,
339: true, installListener);
340: }
341: return suiteId;
342:
343: } catch (Throwable t) {
344: // if retrying, just ignore exception and wait,
345: // otherwise display error message to user.
346: if (retry) {
347: try {
348: Thread.sleep(RETRY_WAIT_TIME);
349: } catch (Exception ex) {
350: }
351: } else {
352: handleInstallerException(suiteId, t);
353: }
354: }
355: }
356:
357: return MIDletSuite.UNUSED_SUITE_ID;
358: }
359: }
360: }
|