1 | /* |
2 | * Created on Aug 31, 2004 |
3 | * |
4 | * TODO To change the template for this generated file go to |
5 | * Window - Preferences - Java - Code Style - Code Templates |
6 | */ |
7 | package biz.xsoftware.buildtemplate; |
8 | |
9 | import java.io.File; |
10 | import java.io.IOException; |
11 | import java.io.InputStream; |
12 | import java.net.InetAddress; |
13 | import java.net.InetSocketAddress; |
14 | import java.net.ServerSocket; |
15 | import java.net.Socket; |
16 | import java.util.logging.Level; |
17 | import java.util.logging.Logger; |
18 | |
19 | /** |
20 | * @author Dean Hiller |
21 | * |
22 | * TODO To change the template for this generated type comment go to |
23 | * Window - Preferences - Java - Code Style - Code Templates |
24 | */ |
25 | public class RunAnt { |
26 | |
27 | private final static Logger log = Logger.getLogger(RunAnt.class.getName()); |
28 | private Process p2 = null; |
29 | private boolean destroyed = false; |
30 | |
31 | public void run(File userDir, String[] args) throws Exception { |
32 | runAntByForkingWithLogger(userDir, args); |
33 | } |
34 | |
35 | synchronized public void destroy() { |
36 | if(p2 != null) |
37 | p2.destroy(); |
38 | destroyed = true; |
39 | } |
40 | |
41 | //can't use this method until jdk1.5 because there is no way to have |
42 | //stdout and stderr be printed in the order ant prints them. |
43 | //with 1.5, the new ProcessBuilder can be used...until then, can't |
44 | //fork ant as a separate process. |
45 | private void runAntByForkingWithLogger(File userDir, String[] args) throws Exception { |
46 | //tried to be lazy but that did not work....got exception in windows |
47 | //which we shouldn't get, must be bug in JVM, we should look in bug |
48 | //db. |
49 | |
50 | //ehhh, I am lazy right now, let's just exec the ant process |
51 | //later on, we could run ant in the same JVM. Actually, running ant in the |
52 | //same JVM doesn't work because on windows, all the jar files become locked |
53 | //and I can't delete them. If I fork ant instead, I can delete the ant |
54 | //jars after I am done so the only thing left in someones view is the |
55 | //buildtemplate.jar plus a few project specific files that came from the template. |
56 | int numOurArgs = 13; |
57 | String[] cmdArray = new String[args.length+numOurArgs]; |
58 | System.arraycopy(args, 0, cmdArray, numOurArgs, args.length); |
59 | |
60 | //Until everyone starts using jdk5.0, we cannot fork the ant process |
61 | //and redirect stdout and stderr to the same stream. Instead, we specify ant |
62 | //to log to our very own AntSocketLogger, so while ant writes to the socket |
63 | //we will be reading from the socket, and when the build is finally finished, |
64 | //AntSocketLogger will close the socket letting us know the build is finished |
65 | InetAddress localHost = InetAddress.getByName("127.0.0.1"); |
66 | ServerSocket s = new ServerSocket(); |
67 | s.bind(new InetSocketAddress(localHost, 0)); |
68 | int port = s.getLocalPort(); |
69 | String host = s.getInetAddress().getHostAddress(); |
70 | log.info("host="+host+" port="+port); |
71 | |
72 | ReadLogFromSocket readThread = new ReadLogFromSocket(s); |
73 | readThread.start(); |
74 | |
75 | //ok, doing a "ant --execdebug" shows the long start ant command |
76 | //only on linux. Windows does not have the same command |
77 | //so we will use what they do to stay platform independent and |
78 | //fork the ant process by calling "java xxxxx xx" |
79 | //NOTE: we have to fork so when the process returns we can delete |
80 | //the ant jars. |
81 | // |
82 | // "/cygdrive/c/ROOT/programs/jdk1.5.0//bin/java" |
83 | // -classpath "c:/ROOT/programs/ant/lib/ant-launcher.jar" |
84 | // -Dant.home="c:/ROOT/programs/ant" |
85 | // -Dant.library.dir="c:/ROOT/programs/ant/lib" |
86 | // -Dcygwin.user.home="C:/ROOT/programs/cygwin/home/Dean Hiller" |
87 | // org.apache.tools.ant.launch.Launcher -lib "" |
88 | |
89 | String f = File.separator; |
90 | String p = File.pathSeparator; |
91 | String base = userDir.getCanonicalPath(); |
92 | String antDir = base+f+Main.TOOLS_DIR+f+"ant"; |
93 | String antLib = antDir+f+"lib"; |
94 | //crap...this will only work on windows, may need to check system |
95 | //properties and if windows, use 'ant.bat', else use 'ant' script instead |
96 | //the other option is to fork java -cp buildtemplate.jar com.avaya.ClassThatRunsAnt |
97 | int j = 0; |
98 | cmdArray[j++] = "java"; |
99 | cmdArray[j++] = "-classpath"; |
100 | cmdArray[j++] = antLib+f+"ant-launcher.jar"; |
101 | cmdArray[j++] = "-Dant.home="+antDir; |
102 | cmdArray[j++] = "-Dant.library.dir="+antLib; |
103 | //cmdArray[j++] = "-Dcygwin.user.home=\""+System.getProperty("user.home")+"\""; |
104 | cmdArray[j++] = "-D"+AntSocketLogger.PORT_PROPERTY+"="+port; |
105 | cmdArray[j++] = "org.apache.tools.ant.launch.Launcher"; |
106 | cmdArray[j++] = "-logger"; |
107 | cmdArray[j++] = "biz.xsoftware.buildtemplate.AntSocketLogger"; |
108 | cmdArray[j++] = "-lib"; |
109 | cmdArray[j++] = base+f+Main.TEMPLATE_JAR_FILE; |
110 | cmdArray[j++] = "-f"; |
111 | cmdArray[j++] = base+f+"bldfiles"+f+"build.xml"; |
112 | |
113 | try { |
114 | Process p1 = Runtime.getRuntime().exec("chmod 755 "+cmdArray[0]); |
115 | p1.waitFor(); |
116 | } catch(Exception e) { /* gulp if on windows */ } |
117 | |
118 | String cmdString = ""; |
119 | for(int i = 0; i < cmdArray.length; i++) { |
120 | cmdString += cmdArray[i]+" "; |
121 | } |
122 | log.info("run ant. cmd="+cmdString); |
123 | log.fine("running ant in dir="+userDir.getCanonicalPath()); |
124 | System.err.println(); |
125 | synchronized(this) { |
126 | if(!destroyed) |
127 | p2 = Runtime.getRuntime().exec(cmdArray); |
128 | } |
129 | //If we fork, we need to deal with stdout and stderr, but we can't |
130 | //print them mixed which sucks. In jdk5.0, we can print them mixed finally |
131 | InputStream in; |
132 | |
133 | log.fine("***********stdout****************"); |
134 | in = p2.getInputStream(); |
135 | Util.copyInToOut(in, System.err); |
136 | in.close(); |
137 | log.fine("***********stderr****************"); |
138 | in = p2.getErrorStream(); |
139 | Util.copyInToOut(in, System.err); |
140 | in.close(); |
141 | log.fine("***********done"); |
142 | |
143 | log.finer("waiting for ant process"); |
144 | //wait until ant is finished....we can wait until we have all the ant logs |
145 | //we do not need to wait for ant's process to finish. should we do that too?? |
146 | //DO NOT SYNCHRONIZE HERE AS DESTORY WILL NOT BE CALLABLE WHILE this is |
147 | //waiting if there is a synchronization lock on this.... |
148 | p2.waitFor(); |
149 | p2 = null; |
150 | log.finer("wait for all read signal"); |
151 | |
152 | //Unfortunately I could not find a way to open a socket in ant with the ant logger |
153 | //before a target started as I needed access to the project properties and some |
154 | //of the other methods in the Superclass Logger did not provide them in ant1.6.2. |
155 | //I think that is a bug with ant, but I did not investigate further into the issue. |
156 | //readThread.waitForAllRead(); |
157 | } |
158 | |
159 | private class ReadLogFromSocket extends Thread { |
160 | private ServerSocket serverSock; |
161 | private boolean allHasBeenRead = false; |
162 | |
163 | /** |
164 | * @param s |
165 | */ |
166 | public ReadLogFromSocket(ServerSocket s) { |
167 | serverSock = s; |
168 | } |
169 | |
170 | /* (non-Javadoc) |
171 | * @see java.lang.Runnable#run() |
172 | */ |
173 | public void run() { |
174 | try { |
175 | readAnt(); |
176 | } catch(Exception e) { |
177 | log.log(Level.WARNING, "Some kind of exception happenend", e); |
178 | } |
179 | } |
180 | synchronized private void readAnt() throws IOException { |
181 | log.finer("accepting incoming socket from ant"); |
182 | Socket s = serverSock.accept(); |
183 | log.finer("accepted socket"); |
184 | |
185 | InputStream in = s.getInputStream(); |
186 | //AntSocketLogger is guaranteed to close the socket thereby releasing |
187 | //copyInToOut and continuing on |
188 | Util.copyInToOut(in, System.err); |
189 | in.close(); |
190 | |
191 | allHasBeenRead = true; |
192 | this.notifyAll(); |
193 | } |
194 | |
195 | synchronized public void waitForAllRead() { |
196 | try { |
197 | if(!allHasBeenRead) { |
198 | this.wait(); |
199 | } |
200 | } catch(InterruptedException e) { |
201 | log.log(Level.WARNING, "exception", e); |
202 | } |
203 | } |
204 | } |
205 | } |