001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Sam
028: */
029:
030: package com.caucho.netbeans;
031:
032: import com.caucho.netbeans.PluginL10N;
033: import com.caucho.netbeans.PluginLogger;
034:
035: import org.openide.execution.NbProcessDescriptor;
036:
037: import java.io.File;
038: import java.io.IOException;
039: import java.io.InputStreamReader;
040: import java.net.ServerSocket;
041: import java.net.InetAddress;
042: import java.util.logging.Level;
043: import java.util.logging.Logger;
044:
045: public class ResinProcess {
046: private static final PluginL10N L = new PluginL10N(
047: ResinProcess.class);
048: private static final Logger log = Logger
049: .getLogger(ResinProcess.class.getName());
050:
051: public static final int LIFECYCLE_NEW = 0;
052: public static final int LIFECYCLE_INITIALIZING = 1;
053: public static final int LIFECYCLE_INIT = 2;
054: public static final int LIFECYCLE_STARTING = 3;
055: public static final int LIFECYCLE_STANDBY = 4;
056: public static final int LIFECYCLE_WARMUP = 5;
057: public static final int LIFECYCLE_ACTIVE = 6;
058: public static final int LIFECYCLE_FAILED = 7;
059: public static final int LIFECYCLE_STOPPING = 8;
060: public static final int LIFECYCLE_STOPPED = 9;
061: public static final int LIFECYCLE_DESTROYING = 10;
062: public static final int LIFECYCLE_DESTROYED = 11;
063:
064: private int _lifecycle = LIFECYCLE_NEW;
065:
066: private static final int TIMEOUT_TICK = 250;
067:
068: private final String _uri;
069: private final ResinConfiguration _resinConfiguration;
070:
071: private boolean _isDebug;
072: private int _activeServerPort;
073: private int _activeDebugPort;
074:
075: private Process _process;
076: private Console _console;
077:
078: private final Object _lock = new Object();
079: private File _javaExe;
080: private File _resinJar;
081:
082: public ResinProcess(String uri,
083: ResinConfiguration resinConfiguration) {
084: _uri = uri;
085: _resinConfiguration = resinConfiguration;
086: }
087:
088: public ResinConfiguration getResinConfiguration() {
089: return _resinConfiguration;
090: }
091:
092: private boolean isInit() {
093: return _lifecycle >= LIFECYCLE_INIT;
094: }
095:
096: public boolean isActive() {
097: return _lifecycle == LIFECYCLE_ACTIVE;
098: }
099:
100: public void init() throws IllegalStateException {
101: synchronized (_lock) {
102: if (isInit())
103: return;
104:
105: _lifecycle = LIFECYCLE_INITIALIZING;
106:
107: _resinConfiguration.validate();
108:
109: File javaExe;
110:
111: File javaHome = _resinConfiguration.calculateJavaHome();
112:
113: javaExe = new File(javaHome, "bin/java");
114:
115: if (!javaExe.exists())
116: javaExe = new File(javaHome, "bin/java.exe");
117:
118: if (!javaExe.exists())
119: throw new IllegalStateException(L.l(
120: "Cannot find java exe in ''{0}''", javaHome));
121:
122: _javaExe = javaExe;
123:
124: File resinHome = _resinConfiguration.getResinHome();
125:
126: File resinJar = new File(resinHome, "lib/resin.jar");
127:
128: if (!resinJar.exists())
129: throw new IllegalStateException(L.l(
130: "Cannot find lib/resin.jar in ''{0}''",
131: resinHome));
132:
133: _resinJar = resinJar;
134:
135: _lifecycle = LIFECYCLE_INIT;
136: }
137: }
138:
139: public void start() throws IllegalStateException, IOException {
140: init();
141:
142: synchronized (_lock) {
143: if (isActive())
144: stopImpl(false);
145:
146: _isDebug = false;
147:
148: startImpl();
149: }
150: }
151:
152: public void startDebug() throws IllegalStateException, IOException {
153: init();
154:
155: synchronized (_lock) {
156: if (isActive())
157: stopImpl(false);
158:
159: _isDebug = true;
160:
161: startImpl();
162: }
163: }
164:
165: public boolean isDebug() {
166: return _isDebug;
167: }
168:
169: private void startImpl() throws IllegalStateException, IOException {
170: _lifecycle = LIFECYCLE_STARTING;
171:
172: int serverPort = _resinConfiguration.getServerPort();
173: int debugPort = _resinConfiguration.getDebugPort();
174: File resinHome = _resinConfiguration.getResinHome();
175: File resinConf = _resinConfiguration.getResinConf();
176: String serverId = _resinConfiguration.getServerId();
177: log.info("Resin starting on " + serverPort);
178: if (!isPortFree(serverPort))
179: throw new IllegalStateException(
180: L
181: .l(
182: "Cannot start Resin, server-port {0} is already in use",
183: serverPort));
184:
185: if (_isDebug) {
186: if (debugPort == 0) {
187: ServerSocket ss = new ServerSocket(0, 5, InetAddress
188: .getByName("127.0.0.1"));
189:
190: debugPort = ss.getLocalPort();
191:
192: try {
193: Thread.sleep(100);
194: } catch (InterruptedException e) {
195: }
196: ss.close();
197: }
198:
199: if (!isPortFree(debugPort))
200: throw new IllegalStateException(
201: L
202: .l(
203: "Cannot start Resin, debug-port {0} is already in use",
204: debugPort));
205: }
206:
207: StringBuilder cp = new StringBuilder();
208: File lib = new File(resinHome, "lib");
209: for (String jar : lib.list()) {
210: if (jar.endsWith(".jar")) {
211: cp.append(File.pathSeparatorChar);
212: cp.append(new File(lib, jar).getAbsolutePath());
213: }
214: }
215:
216: StringBuilder args = new StringBuilder();
217:
218: args.append(" -Dresin.home='" + resinHome + "'");
219: args.append(" com.caucho.resin.ResinEmbed");
220:
221: args.append(" --port=");
222: args.append(serverPort);
223: args.append(" --deploy:role=any");
224:
225: if (_isDebug)
226: throw new IllegalStateException(
227: "debug mode not implemented");
228:
229: // open the ServerLog
230: synchronized (this ) {
231: if (_console != null) {
232: _console.takeFocus();
233: } else {
234: _console = new Console(_uri);
235: }
236: }
237:
238: String classpath = null;
239:
240: String[] envp = new String[] { "CLASSPATH=" + cp };
241:
242: String displayName = _resinConfiguration.getDisplayName();
243:
244: NbProcessDescriptor processDescriptor = new NbProcessDescriptor(
245: _javaExe.getAbsolutePath(), args.toString(),
246: displayName);
247:
248: _console.println(L.l("Starting Resin process {0} {1}",
249: processDescriptor.getProcessName(), processDescriptor
250: .getArguments()));
251:
252: _console.flush();
253:
254: _process = processDescriptor.exec(null, envp, true, resinHome);
255:
256: _console.println();
257:
258: _console.start(
259: new InputStreamReader(_process.getInputStream()),
260: new InputStreamReader(_process.getErrorStream()));
261:
262: new Thread("resin-" + _uri + "-process-monitor") {
263: public void run() {
264: try {
265: _process.waitFor();
266: Thread.sleep(2000);
267: } catch (InterruptedException e) {
268: } finally {
269: handleProcessDied();
270: }
271: }
272: }.start();
273:
274: // wait for server port to become active
275:
276: _activeServerPort = serverPort;
277: _activeDebugPort = debugPort;
278:
279: int startTimeout = _resinConfiguration.getStartTimeout();
280:
281: boolean isResponding = false;
282:
283: for (int i = startTimeout; i > 0; i -= TIMEOUT_TICK) {
284: if (isResponding()) {
285: isResponding = true;
286: break;
287: }
288:
289: try {
290: Thread.sleep(TIMEOUT_TICK);
291: } catch (InterruptedException ex) {
292: log.log(Level.WARNING, ex.toString(), ex);
293: }
294: }
295:
296: if (!isResponding) {
297: String msg = L
298: .l(
299: "Resin process failed to respond on server-port {0}",
300: serverPort);
301:
302: log.log(Level.WARNING, msg);
303:
304: try {
305: stopImpl(false);
306: } catch (Exception ex) {
307: log.log(Level.WARNING, ex.toString(), ex);
308: }
309:
310: throw new IOException(msg);
311: }
312:
313: _lifecycle = LIFECYCLE_ACTIVE;
314: }
315:
316: public Console getConsole() {
317: return _console;
318: }
319:
320: private static boolean isPortFree(int port) {
321: ServerSocket ss = null;
322:
323: try {
324: ss = new ServerSocket(port);
325: return true;
326: } catch (IOException ioe) {
327: return false;
328: } finally {
329: if (ss != null) {
330: try {
331: ss.close();
332: } catch (IOException ex) {
333: }
334: }
335: }
336: }
337:
338: public String getHttpUrl() {
339: return null;
340: }
341:
342: /**
343: * Return true if the process is running
344: */
345: public boolean isProcessRunning() {
346: Process process = _process;
347:
348: if (process != null) {
349: try {
350: process.exitValue();
351:
352: return false;
353: } catch (IllegalThreadStateException e) {
354: return true;
355: }
356: }
357:
358: return false;
359: }
360:
361: /**
362: * Return true if the server is responding
363: */
364: public boolean isResponding() {
365: // XXX: could be more robust, contact the server and make sure there is a response
366: return _activeServerPort > 0 && !isPortFree(_activeServerPort);
367: }
368:
369: public Process getJavaProcess() {
370: return _process;
371: }
372:
373: private void handleProcessDied() {
374: stopImpl(false);
375: }
376:
377: public void stop() {
378: synchronized (_lock) {
379:
380: if (_lifecycle != LIFECYCLE_ACTIVE)
381: return;
382:
383: stopImpl(true);
384: }
385: }
386:
387: private void stopImpl(boolean isGraceful) {
388: _lifecycle = LIFECYCLE_STOPPING;
389:
390: Process process = _process;
391: _process = null;
392:
393: Console console = _console;
394: _console = null;
395:
396: int activeServerPort = _activeServerPort;
397: _activeServerPort = 0;
398:
399: int activeDebugPort = _activeDebugPort;
400: _activeDebugPort = 0;
401:
402: _isDebug = false;
403:
404: try {
405: // XXX: graceful shutdown, send message to server,
406: // then use isPortFree in a while loop that times out
407:
408: /*
409: if (isGraceful) {
410: try {
411: printConsoleLine(L.l("Stopping Resin process ..."));
412: }
413: catch (Exception ex) {
414: // no-op
415: }
416:
417: for (int i = STOP_TIMEOUT; !isPortFree(activeServerPort) && i > 0; i-= TICK) {
418: try {
419: Thread.sleep(TICK);
420: }
421: catch (InterruptedException ex) {
422: if (log.isLoggable(Level.WARNING))
423: log.log(Level.WARNING, e), ex);
424:
425: }
426: }
427: }
428: */
429: } finally {
430:
431: try {
432: if (process != null)
433: process.destroy();
434: } finally {
435: try {
436: console.println(L.l("Resin process destroyed"));
437: console.flush();
438: } catch (Exception ex) {
439: // no-op
440: }
441:
442: if (console != null)
443: console.destroy();
444: }
445: }
446:
447: _lifecycle = LIFECYCLE_STOPPED;
448: }
449:
450: public void destroy() {
451: synchronized (_lock) {
452: _lifecycle = LIFECYCLE_DESTROYING;
453:
454: try {
455: try {
456: stop();
457: } catch (Exception ex) {
458: log.log(Level.WARNING, ex.toString(), ex);
459:
460: try {
461: stopImpl(false);
462: } catch (Exception ex2) {
463: log.log(Level.WARNING, ex2.toString(), ex);
464: }
465: }
466: } finally {
467: _lifecycle = LIFECYCLE_DESTROYED;
468: }
469: }
470: }
471: }
|