001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.j2me.cdc.project.nsicom;
043:
044: // IMPORTANT! You need to compile this class against ant.jar. So add the
045: // JAR ide5/ant/lib/ant.jar from your IDE installation directory (or any
046: // other version of Ant you wish to use) to your classpath. Or if
047: // writing your own build target, use e.g.:
048: // <classpath>
049: // <pathelement location="${ant.home}/lib/ant.jar"/>
050: // </classpath>
051:
052: import java.io.BufferedReader;
053: import java.io.File;
054: import java.io.FileOutputStream;
055: import java.io.IOException;
056: import java.io.InputStream;
057: import java.io.InputStreamReader;
058: import java.io.PrintWriter;
059: import java.io.StringWriter;
060: import java.util.ArrayList;
061: import java.util.Iterator;
062: import java.util.LinkedList;
063: import java.util.List;
064: import java.util.StringTokenizer;
065: import org.apache.tools.ant.*;
066: import org.apache.tools.ant.types.*;
067:
068: import org.netbeans.mobility.activesync.*;
069: import org.openide.util.Exceptions;
070:
071: /**
072: * @author suchys
073: */
074: public class NSIcomExecDeployTask extends Task {
075:
076: private List filesets = new LinkedList(); // List<FileSet>
077: private File home;
078: private String mainclass;
079: private String args;
080: private String jvmargs;
081: private String device;
082: private String profile;
083: private boolean xlet;
084: private boolean applet;
085:
086: private boolean verbose;
087: private String hostIP;
088:
089: //for debugger
090: private String debuggerAddressProperty;
091: private boolean debug;
092:
093: private boolean runOnDevice;
094: private String remoteVMLocation;
095: private String remoteDataLocation;
096:
097: private boolean deploy;
098: //out stream
099: protected PrintWriter fos = null;
100:
101: //activesync
102: private ActiveSyncOps activeSync;
103:
104: private RemoteFile remoteFolder = null; //not a property
105:
106: public void addFileset(FileSet fs) {
107: filesets.add(fs);
108: }
109:
110: public void execute() throws BuildException {
111: if (deploy) {
112: runOnDevice = true; //deploy is always on device operation
113: }
114:
115: if (runOnDevice) {
116: try {
117: //URL location = ActiveSyncOps.class.getClass().getProtectionDomain().getCodeSource().getLocation();
118: activeSync = ActiveSyncOps.getDefault();
119: log("Library nbactivesync.dll loaded correctly",
120: Project.MSG_VERBOSE); //NOI18N only internal debug
121: } catch (Throwable e) {
122: throw new BuildException(
123: "Library nbactivesync.dll can not be loaded!"); //NOI18N
124: }
125:
126: checkActiveSync();
127: }
128:
129: List arguments = new ArrayList();
130: if (!runOnDevice) {
131: String os = System.getProperty("os.name");//NOI18N
132: if (os.toLowerCase().indexOf("windows") >= 0) {//NOI18N
133: arguments.add(home + File.separator + "bin"
134: + File.separator + "pJSCP.exe");//NOI18N
135: } else {
136: throw new BuildException(
137: "Only Windows version is supported!");//NOI18N
138: }
139: } else {
140: if (!deploy) { //if not deploying, verify VM
141: verifyRemoteVM();
142: }
143:
144: if (remoteDataLocation == null
145: || remoteDataLocation.length() == 0)
146: throw new BuildException("Remote folder must be set!"); //NOI18N
147: }
148:
149: if (jvmargs != null && jvmargs.trim().length() != 0) {
150: StringTokenizer st = new StringTokenizer(jvmargs, " ");
151: while (st.hasMoreTokens()) {
152: arguments.add(st.nextToken());
153: }
154: }
155:
156: if (!runOnDevice) { //add TCK argument to not to stop VM and wait for key in case of exception
157: arguments.add("-TCK");
158: }
159: //if you are running in debug mode or on device, on device the atribute is mandatory
160: if (debug || runOnDevice) {
161: arguments.add("-mon"); //NOI18N //run monitoring (mandatoty for on device VM
162: if (hostIP != null && hostIP.length() != 0) {
163: arguments.add(hostIP);
164: }
165: if (runOnDevice) {
166: arguments.add("\'-Of" + remoteDataLocation + "\'"); //NOI18N //create log file (only device)
167: }
168: } else {
169: arguments.add("-Ob"); //NOI18N //sent output to console (only emulator)
170: }
171:
172: if (verbose)
173: arguments.add("-verbose"); //NOI18N
174:
175: if (!xlet && !applet) { //for main
176: arguments.add("-classpath");//NOI18N
177: arguments.add(createClassPath());
178: arguments.add(mainclass);
179: appendArguments(arguments);
180: } else if (xlet) { //xlet
181: assert true : "Xlet execution is not supported!"; //NOI18N
182: } else { //applet
183: arguments.add("-av"); //NOI18N //run applet argument
184: //no handling for arguments in generateHtml!!
185: //will copy classes to remote device as well by call generateHtml(..)
186: String html = generateHtml();
187: File f = new File(this .getProject().getBaseDir().toString()
188: + "/build", "applet.html"); //NOI18N
189: try {
190: FileOutputStream dos = new FileOutputStream(f);
191: dos.write(html.getBytes());
192: dos.close();
193: } catch (IOException ex) {
194: ex.printStackTrace();
195: }
196:
197: if (!runOnDevice) {
198: arguments.add(f.toString());
199: } else {
200: try {
201: remoteFolder = createRemoteFolder(remoteDataLocation);
202: } catch (Exception e) {
203: throw new BuildException(
204: "Remote folder can not be created!"); //NOI18N
205: }
206: try {
207: RemoteFile remoteHtmlItem = new RemoteFile(
208: remoteFolder.getFullPath(), "applet.html"); //NOI18N
209: if (remoteHtmlItem.exists()) {
210: activeSync.delete(remoteHtmlItem);
211: }
212: activeSync.copyToDevice(f, remoteHtmlItem);
213: arguments.add("\'" + remoteHtmlItem.getFullPath()
214: + "\'");
215: } catch (IOException ioEx) {
216: throw new BuildException(
217: "Can not create applet.html"); //NOI18N
218: }
219: }
220: }
221:
222: if (!deploy) { //deployment does not execute
223: String[] arg = (String[]) arguments.toArray(new String[0]);
224: getProject().log("Application arguments:",
225: Project.MSG_VERBOSE); //NOI18N only internal debug
226: for (int i = 0; i < arg.length; i++) {
227: getProject().log("'" + arg[i] + "'",
228: Project.MSG_VERBOSE); //NOI18N only internal debug
229: }
230:
231: try {
232: if (!runOnDevice) {
233: Process p = null;
234: try {
235: p = Runtime.getRuntime().exec(arg);
236: StreamReader inputReader = new StreamReader(p
237: .getInputStream(), Project.MSG_INFO);
238: StreamReader errorReader = new StreamReader(p
239: .getErrorStream(), Project.MSG_WARN);
240:
241: // starts pumping away the generated output/error
242: inputReader.start();
243: errorReader.start();
244:
245: // Wait for everything to finish
246: p.waitFor();
247: inputReader.join();
248: errorReader.join();
249: } catch (ThreadDeath td) {
250: p.destroy();
251: throw td;
252: } finally {
253: // close the output file if required
254: logFlush();
255: }
256: if (p.exitValue() != 0)
257: throw new BuildException(
258: "Emulator execution failed!"); //NOI18N
259: } else {
260: //clean up log file
261: RemoteFile root = activeSync.getRootFilesystems()[0];
262: //RemoteFile log = new RemoteFile(root.getFullPath(), "jspcout.txt");//NOI18N
263: RemoteFile log = new RemoteFile(remoteFolder
264: .getFullPath(), "jspcout.txt");
265: try {
266: if (log.exists()) {
267: activeSync.delete(log);
268: }
269: } catch (Exception e) {
270: //ignore
271: log("Can not delete log file",
272: Project.MSG_VERBOSE); //NOI18N only internal debug
273: }
274: ProcessHandler handler = null;
275: try {
276: handler = new ProcessHandler(
277: remoteDataLocation, arg, remoteFolder);
278: handler.start();
279: try {
280: handler.join();
281: } catch (InterruptedException ie) {
282: }
283: if (handler.getExitCode() != 0) {
284: throw handler.getCause();
285: }
286: } catch (ThreadDeath threadDeath) { //ant task has been shut down
287: if (handler.getProcess() != null)
288: handler.destroyRemoteProcess();
289: throw threadDeath;
290: }
291: }
292: } catch (IOException ex) {
293: throw new BuildException("Emulator execution failed!");//NOI18N
294: } catch (InterruptedException ex) {
295: throw new BuildException("Emulator execution failed!");//NOI18N
296: }
297: }
298: }
299:
300: private String generateHtml() throws BuildException {
301: //assert true : "Not finished yet.";
302: StringWriter sw = new StringWriter();
303: PrintWriter pw = null;
304: pw = new PrintWriter(sw);//new FileWriter(appletHtml));
305: pw.println("<HTML>"); //NOI18N
306: pw.println("<HEAD>"); //NOI18N
307: pw.println("<TITLE> A Testing Program for " + mainclass
308: + " </TITLE>"); //NOI18N
309: pw.println("</HEAD>"); //NOI18N
310: pw.println("<BODY>"); //NOI18N
311: pw.println("<APPLET CODE=\"" + mainclass
312: + ".class\" WIDTH=250 HEIGHT=350>"); //NOI18N
313: //will copy classes to remote device as well
314: pw.println("<PARAM NAME=ARCHIVE VALUE=" + createClassPath()
315: + "/>");//NOI18N
316: pw.println("</APPLET>"); //NOI18N
317: pw.println("</BODY>"); //NOI18N
318: pw.println("</HTML>"); //NOI18N
319: pw.flush();
320: return sw.getBuffer().toString();
321: }
322:
323: private String createClassPath() {
324: Iterator it = filesets.iterator();
325: StringBuffer sb = new StringBuffer();
326: while (it.hasNext()) {
327: FileSet fs = (FileSet) it.next();
328: DirectoryScanner ds = fs.getDirectoryScanner(project);
329: File basedir = ds.getBasedir();
330: String[] files = ds.getIncludedFiles();
331: for (int i = 0; i < files.length; i++) {
332: if (!runOnDevice) {
333: if (!applet) { //look at different classpath quotation for PC and device run
334: sb.append("\"" + basedir.getAbsolutePath()
335: + File.separatorChar + files[i] + "\""); //NOI18N
336: } else {
337: sb.append("../dist/" + files[i]); //NOI18N
338: }
339: if (i + 1 < files.length) {
340: sb.append(";");
341: }
342: } else {
343: //for on device we must copy classpath items to remote destination
344: try {
345: remoteFolder = createRemoteFolder(remoteDataLocation);
346: } catch (Exception e) {
347: throw new BuildException(
348: "Remote folder can not be created!"); //NOI18N
349: }
350: try {
351: RemoteFile remoteCpItem = new RemoteFile(
352: remoteFolder.getFullPath(), files[i]);
353: if (remoteCpItem.exists()) {
354: activeSync.delete(remoteCpItem);
355: }
356: activeSync.copyToDevice(new File(basedir
357: .getAbsolutePath(), files[i]),
358: remoteCpItem);
359: log(
360: "Copy classpath element " + files[i]
361: + " into "
362: + remoteFolder.getFullPath(),
363: Project.MSG_VERBOSE); //NOI18N only internal debug
364: } catch (IOException ex) {
365: throw new BuildException(
366: "Can not copy classpath " + files[i]
367: + " item to remote device!"); //NOI18N
368: }
369:
370: if (!applet) {//look at different classpath quotation for PC and device run
371: sb.append("\'" + remoteFolder.getFullPath()
372: + File.separatorChar + files[i] + "\'"); //NOI18N
373: } else {
374: sb.append(files[i]);//this will not work for more than one file
375: }
376: if (i + 1 < files.length) {
377: sb.append(";"); //NOI18N
378: }
379: }
380: }
381: }
382: return sb.toString();
383: }
384:
385: private void appendArguments(List args) {
386: if (this .args == null || this .args.length() == 0)
387: return;
388: StringTokenizer st = new StringTokenizer(this .args, " "); //NOI18N
389: while (st.hasMoreTokens()) {
390: //args.add("-D" + st.nextToken());
391: args.add(st.nextToken());
392: }
393: }
394:
395: private void outputLog(String line, int messageLevel) {
396: if (fos == null) {
397: log(line, messageLevel);
398: } else {
399: fos.println(line);
400: }
401: }
402:
403: private void logFlush() {
404: if (fos != null) {
405: fos.close();
406: }
407: }
408:
409: //** remote device operations
410: private void checkActiveSync() throws BuildException {
411: if (!activeSync.isAvailable())
412: throw new BuildException("ActiveSync is not installed!"); //NOI18N
413: log("Active sync available", Project.MSG_VERBOSE); //NOI18N only internal debug
414:
415: try {
416: if (!activeSync.isDeviceConnected())
417: throw new BuildException("Device is not connected!"); //NOI18N
418: log("Device connected", Project.MSG_VERBOSE); //NOI18N only internal debug
419: } catch (ActiveSyncException ex) {
420: throw new BuildException(
421: "Error during connecting to remote device!"); //NOI18N
422: }
423: }
424:
425: private void verifyRemoteVM() throws BuildException {
426: if (remoteVMLocation == null || remoteVMLocation.length() == 0)
427: throw new BuildException("Remote VM location must be set!"); //NOI18N
428:
429: RemoteFile remoteVM = new RemoteFile(remoteVMLocation, ""); //NOI18N
430: if (!remoteVM.exists() || remoteVM.isDirectory())
431: throw new BuildException(
432: "Remote VM does not exist in specified location: "
433: + remoteVM.getFullPath()); //NOI18N
434: log("Remote VM OK", Project.MSG_VERBOSE); //NOI18N only internal debug
435:
436: }
437:
438: private RemoteFile createRemoteFolder(String name)
439: throws ActiveSyncException, IOException {
440: StringTokenizer st = new StringTokenizer(name, "/\\"); //NOI18N
441: RemoteFile root = activeSync.getRootFilesystems()[0];
442: while (st.hasMoreElements()) {
443: name = st.nextToken();
444:
445: if (name.length() > 0) {
446: RemoteFile next = new RemoteFile(root.getFullPath(),
447: name);
448: if (!next.exists()) {
449: next = activeSync.createNewDirectory(root
450: .getFullPath(), name);
451: }
452: root = next;
453: }
454: }
455: log("Remote folder successfuly created", Project.MSG_VERBOSE); //NOI18N only internal debug
456: return root;
457: }
458:
459: private class RemoteLogReader extends Thread {
460: private RemoteFile logFile;
461: private boolean running = true;
462:
463: RemoteLogReader(RemoteFile logFile) {
464: this .logFile = logFile;
465: setDaemon(true);
466: }
467:
468: public void run() {
469: BufferedReader br = null;
470: while (running) {
471: if (br == null
472: && !new RemoteFile(logFile.getFullPath(),
473: "jspcout.txt").exists()) {
474: try {
475: Thread.sleep(50); //wait for VM to create log
476: } catch (InterruptedException ex) {
477: }
478: continue;
479: }
480: try {
481: if (br == null) {
482: InputStream is = activeSync
483: .getRemoteInputStream(new RemoteFile(
484: logFile.getFullPath(),
485: "jspcout.txt"));
486: if (is != null) {
487: br = new BufferedReader(
488: new InputStreamReader(is));
489: }
490: } else {
491: String line = br.readLine();
492: if (line != null) {
493: log(line);
494: }
495: }
496: Thread.sleep(5);
497: } catch (InterruptedException ex) {
498: ex.printStackTrace();
499: } catch (IOException ex) {
500: ex.printStackTrace();
501: }
502: }
503: }
504:
505: public void finish() {
506: running = false;
507: }
508: }
509:
510: //** end
511:
512: public File getHome() {
513: return home;
514: }
515:
516: public void setHome(File home) {
517: this .home = home;
518: }
519:
520: public String getMainclass() {
521: return mainclass;
522: }
523:
524: public void setMainclass(String mainclass) {
525: this .mainclass = mainclass;
526: }
527:
528: public String getArgs() {
529: return args;
530: }
531:
532: public void setArgs(String args) {
533: this .args = args;
534: }
535:
536: public String getJvmargs() {
537: return jvmargs;
538: }
539:
540: public void setJvmargs(String jvmargs) {
541: this .jvmargs = jvmargs;
542: }
543:
544: public String getDevice() {
545: return device;
546: }
547:
548: public void setDevice(String device) {
549: this .device = device;
550: }
551:
552: public boolean isXlet() {
553: return xlet;
554: }
555:
556: public void setXlet(boolean xlet) {
557: this .xlet = xlet;
558: }
559:
560: public boolean isApplet() {
561: return applet;
562: }
563:
564: public void setApplet(boolean applet) {
565: this .applet = applet;
566: }
567:
568: public String getDebuggerAddressProperty() {
569: return debuggerAddressProperty;
570: }
571:
572: public void setDebuggerAddressProperty(
573: String debuggerAddressProperty) {
574: this .debuggerAddressProperty = debuggerAddressProperty;
575: }
576:
577: public boolean isDebug() {
578: return debug;
579: }
580:
581: public void setDebug(boolean debug) {
582: this .debug = debug;
583: }
584:
585: class StreamReader extends Thread {
586: private BufferedReader din;
587: private int messageLevel;
588: private boolean endOfStream = false;
589: private int SLEEP_TIME = 5;
590:
591: public StreamReader(InputStream is, int messageLevel) {
592: this .din = new BufferedReader(new InputStreamReader(is));
593: this .messageLevel = messageLevel;
594: }
595:
596: public void pumpStream() throws IOException {
597: if (!endOfStream) {
598: String line = din.readLine();
599:
600: if (line != null) {
601: outputLog(line, messageLevel);
602: } else {
603: endOfStream = true;
604: }
605: }
606: }
607:
608: public void run() {
609: try {
610: try {
611: while (!endOfStream) {
612: pumpStream();
613: sleep(SLEEP_TIME);
614: }
615: } catch (InterruptedException ie) {
616: }
617: din.close();
618: } catch (IOException ioe) {
619: }
620: }
621: }
622:
623: public String getProfile() {
624: return profile;
625: }
626:
627: public void setProfile(String profile) {
628: this .profile = profile;
629: }
630:
631: public boolean isVerbose() {
632: return verbose;
633: }
634:
635: public void setVerbose(boolean verbose) {
636: this .verbose = verbose;
637: }
638:
639: public String getHostIP() {
640: return hostIP;
641: }
642:
643: public void setHostIP(String hostIP) {
644: this .hostIP = hostIP;
645: }
646:
647: public boolean isRunOnDevice() {
648: return runOnDevice;
649: }
650:
651: public void setRunOnDevice(boolean runOnDevice) {
652: this .runOnDevice = runOnDevice;
653: }
654:
655: public String getRemoteVMLocation() {
656: return remoteVMLocation;
657: }
658:
659: public void setRemoteVMLocation(String remoteVMLocation) {
660: this .remoteVMLocation = remoteVMLocation;
661: }
662:
663: public String getRemoteDataLocation() {
664: return remoteDataLocation;
665: }
666:
667: public void setRemoteDataLocation(String remoteDataLocation) {
668: this .remoteDataLocation = remoteDataLocation;
669: }
670:
671: public boolean isDeploy() {
672: return deploy;
673: }
674:
675: public void setDeploy(boolean deploy) {
676: this .deploy = deploy;
677: }
678:
679: class ProcessHandler extends Thread {
680:
681: private RemoteProcess process;
682: private int exitCode = -1;
683: private IOException cause = null;
684: private RemoteFile remoteFolder;
685: private String[] vmArguments;
686: private String vmLocation;
687: private RemoteLogReader remoteReader = null;
688:
689: public ProcessHandler(String vmLocation, String[] vmArguments,
690: RemoteFile remoteFolder) {
691: this .vmLocation = vmLocation;
692: this .vmArguments = vmArguments;
693: this .remoteFolder = remoteFolder;
694: setDaemon(true);
695: }
696:
697: public void run() {
698: try {
699: process = activeSync.executeRemoteProcess(
700: remoteVMLocation, vmArguments);
701: remoteReader = new RemoteLogReader(remoteFolder);
702: remoteReader.start();
703: exitCode = activeSync.waitFor(process);
704: } catch (ActiveSyncException asEx) {
705: cause = asEx;
706: } catch (IOException ioEx) {
707: cause = ioEx;
708: } finally {
709: if (remoteReader != null)
710: remoteReader.finish();
711: }
712: }
713:
714: public int getExitCode() {
715: return exitCode;
716: }
717:
718: public IOException getCause() {
719: return cause;
720: }
721:
722: public RemoteProcess getProcess() {
723: return process;
724: }
725:
726: private void destroyRemoteProcess() {
727: try {
728: if (process != null)
729: activeSync.destroyRemoteProcess(process);
730: } catch (ActiveSyncException ex) {
731: } catch (IllegalArgumentException ex) {
732: } finally {
733: if (remoteReader != null)
734: remoteReader.finish();
735: }
736: }
737: }
738: }
|