KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > carol > jtests > conform > util > ProcessRunner


1 /**
2  * Copyright (C) 2005 - Red Hat, Inc. All rights reserved.
3  *
4  * CAROL: Common Architecture for RMI ObjectWeb Layer
5  *
6  * This library is developed inside the ObjectWeb Consortium,
7  * http://www.objectweb.org
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22  * USA
23  *
24  * --------------------------------------------------------------------------
25  * $Id: ProcessRunner.java,v 1.4 2005/02/11 10:12:59 benoitf Exp $
26  * --------------------------------------------------------------------------
27  */

28 package org.objectweb.carol.jtests.conform.util;
29
30 import java.io.BufferedReader JavaDoc;
31 import java.io.File JavaDoc;
32 import java.io.IOException JavaDoc;
33 import java.io.InputStreamReader JavaDoc;
34 import java.io.UnsupportedEncodingException JavaDoc;
35 import java.lang.reflect.InvocationTargetException JavaDoc;
36 import java.lang.reflect.Method JavaDoc;
37 import java.net.ServerSocket JavaDoc;
38 import java.net.Socket JavaDoc;
39
40 /**
41  * Calls the <code>main()</code> method of the specified Java class and waits
42  * for a shutdown command from {@link ProcessStopper}.
43  * <p>
44  * This allows you to programmatically shut down Java processes that don't
45  * normally provide a way of doing that.
46  * </p>
47  * <p>
48  * Upon starting the process, <code>ProcessRunner</code> binds to a specified
49  * TCP port waiting for a shutdown command from {@linkProcessStopper}.
50  * </p>
51  * <p>
52  * Upon receiving the shutdown command, <code>ProcessRunner</code> exits, thus
53  * stopping the previously started process and touches a file whose name is
54  * specified by the <code>process.runner.exit.stamp</code> property. This file
55  * signals successful shutdown and its presence can be used as a condition by
56  * Ant's <a HREF="http://ant.apache.org/manual/CoreTasks/waitfor.html">WaitFor
57  * </a> task.
58  * </p>
59  * @author Vadim Nasardinov (vadimn@redhat.com)
60  * @since 2005-01-04
61  * @see ProcessStopper
62  */

63 public class ProcessRunner {
64
65     /**
66      * Encoding value
67      */

68     public static final String JavaDoc STREAM_ENCODING = "US-ASCII";
69
70     /**
71      * Command used to shutdown
72      */

73     public static final String JavaDoc SHUTDOWN_COMMAND = "shutdown";
74
75     /**
76      * No default constructor, utility class
77      */

78     private ProcessRunner() {
79
80     }
81
82     /**
83      * Main method
84      * @param args arguments of the program
85      * @throws IOException
86      */

87     public static void main(String JavaDoc[] args) {
88         deleteExitStamp();
89
90         final int nServerArgs = args.length - 1;
91         if (nServerArgs < 0) {
92             throw new IllegalArgumentException JavaDoc("server class name expected");
93         }
94         final String JavaDoc serverClass = args[0];
95         final String JavaDoc[] serverArgs = new String JavaDoc[nServerArgs];
96         System.arraycopy(args, 1, serverArgs, 0, nServerArgs);
97
98         final int remaining = getNShutdowns();
99         final Runner runner = new Runner(serverClass, serverArgs, remaining);
100         runner.start();
101
102         int listenerPort = getListenerPort();
103
104         ServerSocket JavaDoc listenerSocket;
105         try {
106             listenerSocket = new ServerSocket JavaDoc(listenerPort);
107         } catch (IOException JavaDoc ex) {
108             throw new RuntimeException JavaDoc("Couldn't bind to " + listenerPort, ex);
109         }
110
111         try {
112             for (int ii = 0; ii < remaining; ii++) {
113                 new Listener(listenerPort, listenerSocket.accept(), runner).start();
114             }
115         } catch (IOException JavaDoc ex) {
116             throw new RuntimeException JavaDoc("Error accepting connection", ex);
117         }
118     }
119
120     private static String JavaDoc getExitStamp() {
121         String JavaDoc exitStamp = System.getProperty("process.runner.exit.stamp");
122         if ("".equals(exitStamp)) {
123             exitStamp = null;
124         }
125         return exitStamp;
126     }
127
128     private static void deleteExitStamp() {
129         final String JavaDoc exitStamp = getExitStamp();
130
131         if (exitStamp != null) {
132             File JavaDoc exitStampFile = new File JavaDoc(exitStamp);
133             if (exitStampFile.exists()) {
134                 exitStampFile.delete();
135             }
136         }
137     }
138
139     private static int getListenerPort() {
140         String JavaDoc TCP_LISTENER_PORT = "process.runner.tcp.port";
141
142         String JavaDoc port = System.getProperty(TCP_LISTENER_PORT);
143         if (port == null) {
144             throw new IllegalArgumentException JavaDoc("The " + TCP_LISTENER_PORT + " property not set");
145         }
146         try {
147             return Integer.parseInt(port);
148         } catch (NumberFormatException JavaDoc ex) {
149             throw (IllegalArgumentException JavaDoc) new IllegalArgumentException JavaDoc("The value of " + TCP_LISTENER_PORT
150                     + " is not a integer: " + port).initCause(ex);
151         }
152     }
153
154     private static int getNShutdowns() {
155         String JavaDoc N_SHUTDOWNS = "process.runner.n.shutdowns";
156
157         String JavaDoc nShutdowns = System.getProperty(N_SHUTDOWNS);
158         if (nShutdowns == null) {
159             throw new IllegalArgumentException JavaDoc("The " + N_SHUTDOWNS + " property not set");
160         }
161         try {
162             return Integer.parseInt(nShutdowns);
163         } catch (NumberFormatException JavaDoc ex) {
164             throw (IllegalArgumentException JavaDoc) new IllegalArgumentException JavaDoc("The value of " + N_SHUTDOWNS
165                     + " is not a integer: " + nShutdowns).initCause(ex);
166         }
167     }
168
169     private static class Listener extends Thread JavaDoc {
170
171         private Socket JavaDoc m_socket;
172
173         private Runner m_runner;
174
175         Listener(int port, Socket JavaDoc socket, Runner runner) {
176             m_socket = socket;
177             m_runner = runner;
178         }
179
180         public void run() {
181             try {
182                 BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(m_socket.getInputStream(),
183                         STREAM_ENCODING));
184                 String JavaDoc command;
185                 while (null != (command = reader.readLine())) {
186                     if (SHUTDOWN_COMMAND.equals(command)) {
187                         possiblyShutdown();
188
189                         try {
190                             m_socket.close();
191                         } catch (IOException JavaDoc ex) {
192                             ;
193                         }
194                         break;
195                     }
196                 }
197             } catch (UnsupportedEncodingException JavaDoc ex) {
198                 throw new RuntimeException JavaDoc("can't happen", ex);
199             } catch (IOException JavaDoc ex) {
200                 throw new RuntimeException JavaDoc(ex);
201             }
202         }
203
204         private void possiblyShutdown() {
205             synchronized (m_runner) {
206                 m_runner.decrementRemaining();
207                 if (!m_runner.hasRemaining()) {
208                     final String JavaDoc exitStamp = getExitStamp();
209                     if (exitStamp != null) {
210                         File JavaDoc exitStampFile = new File JavaDoc(exitStamp);
211                         try {
212                             exitStampFile.createNewFile();
213                         } catch (IOException JavaDoc ex) {
214                             throw new RuntimeException JavaDoc("Couldn't create " + exitStamp, ex);
215                         }
216                     }
217                     System.exit(0);
218                 }
219             }
220         }
221     }
222
223     private static class Runner extends Thread JavaDoc {
224
225         private final String JavaDoc[] m_serverArgs;
226
227         private final Method JavaDoc m_main;
228
229         private int m_nRemaining;
230
231         Runner(String JavaDoc serverClassname, String JavaDoc[] serverArgs, int remaining) {
232             m_nRemaining = remaining;
233             m_serverArgs = serverArgs;
234
235             final Class JavaDoc serverClass;
236             try {
237                 serverClass = Class.forName(serverClassname);
238             } catch (ClassNotFoundException JavaDoc ex) {
239                 throw new RuntimeException JavaDoc(serverClassname, ex);
240             }
241
242             try {
243                 m_main = serverClass.getMethod("main", new Class JavaDoc[] {String JavaDoc[].class});
244             } catch (NoSuchMethodException JavaDoc ex) {
245                 throw new RuntimeException JavaDoc(serverClassname + " does not have a main method", ex);
246             } catch (SecurityException JavaDoc ex) {
247                 throw new RuntimeException JavaDoc(serverClassname + " does not have a public main method", ex);
248             }
249             setDaemon(true);
250         }
251
252         /**
253          * Not a thread-safe method. Caller must synchronize.
254          */

255         public void decrementRemaining() {
256             m_nRemaining--;
257         }
258
259         /**
260          * Not a thread-safe method. Caller must synchronize.
261          */

262         public boolean hasRemaining() {
263             return m_nRemaining > 0;
264         }
265
266         public void run() {
267             try {
268                 m_main.invoke(null, new Object JavaDoc[] {m_serverArgs});
269             } catch (IllegalAccessException JavaDoc ex) {
270                 die(ex);
271             } catch (IllegalArgumentException JavaDoc ex) {
272                 die(ex);
273             } catch (InvocationTargetException JavaDoc ex) {
274                 die(ex);
275             }
276         }
277
278         private static void die(Exception JavaDoc ex) {
279             throw new RuntimeException JavaDoc(ex);
280         }
281     }
282 }
Popular Tags