001: /*
002: * $Id: JettyMonitor.java 461192 2006-06-28 06:37:16Z ehillenius $ $Revision:
003: * 3905 $ $Date: 2006-06-28 08:37:16 +0200 (Wed, 28 Jun 2006) $
004: *
005: * ====================================================================
006: * Copyright (c) 2003, Open Edge B.V. All rights reserved. Redistribution and
007: * use in source and binary forms, with or without modification, are permitted
008: * provided that the following conditions are met: Redistributions of source
009: * code must retain the above copyright notice, this list of conditions and the
010: * following disclaimer. Redistributions in binary form must reproduce the above
011: * copyright notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution. Neither
013: * the name of OpenEdge B.V. nor the names of its contributors may be used to
014: * endorse or promote products derived from this software without specific prior
015: * written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
020: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
021: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
022: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
023: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
024: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
025: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
026: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
027: * POSSIBILITY OF SUCH DAMAGE.
028: */
029: package nl.openedge.util.jetty;
030:
031: import java.io.IOException;
032: import java.io.InputStream;
033: import java.io.InputStreamReader;
034: import java.io.LineNumberReader;
035: import java.io.OutputStream;
036: import java.net.InetAddress;
037: import java.net.InetSocketAddress;
038: import java.net.ServerSocket;
039: import java.net.Socket;
040: import java.nio.channels.ServerSocketChannel;
041: import java.nio.channels.SocketChannel;
042:
043: import org.apache.commons.logging.Log;
044: import org.apache.commons.logging.LogFactory;
045: import org.mortbay.jetty.Server;
046:
047: /**
048: * Monitor thread. This thread listens on the port specified by the STOP.PORT
049: * system parameter (defaults to 8079) for request authenticated with the key
050: * given by the STOP.KEY system parameter (defaults to "mortbay") for admin
051: * requests. Commands "stop" and "status" are currently supported. Based on
052: * Monitor from JettyServer (start and stop classes).
053: *
054: * @author Eelco Hillenius
055: */
056: public class JettyMonitor extends Thread {
057:
058: /** Listen port. */
059: private int monitorPort;
060:
061: /** Auth key. */
062: private String commKey;
063:
064: /** Log. */
065: private static final Log log = LogFactory
066: .getLog(JettyMonitor.class);
067:
068: /** socket chanel for commands. */
069: private ServerSocketChannel serverSocketChanel = null;
070:
071: /** JettyServer instantie, zodat we (nog) schoner kunnen afsluiten. */
072: private Server server = null;
073:
074: /**
075: * Hidden constructor.
076: *
077: * @param commKey
078: * auth key
079: * @param monitorPort
080: * monitor port
081: */
082: private JettyMonitor(String commKey, int monitorPort) {
083: this .commKey = commKey;
084: this .monitorPort = monitorPort;
085: }
086:
087: /**
088: * @see java.lang.Runnable#run()
089: */
090: public void run() {
091: log.info("Starting Jetty Monitor on port " + monitorPort);
092: createServerSocket();
093: log.info("Socket created");
094: // listen to incomming connections until stop command is issued (method
095: // blocks)
096: listen();
097: try {
098: // exiting... close server socket
099: serverSocketChanel.close();
100: } catch (IOException e) {
101: log.error(e.getMessage(), e);
102: }
103: log.info("exiting monitor and VM");
104: System.exit(0);
105: }
106:
107: /**
108: * Create the server socket.
109: */
110: private void createServerSocket() {
111: try {
112: ServerSocket internalSocket = null;
113: InetSocketAddress socketAddress = new InetSocketAddress(
114: InetAddress.getByName("127.0.0.1"), monitorPort);
115: serverSocketChanel = ServerSocketChannel.open();
116: internalSocket = serverSocketChanel.socket();
117: internalSocket.bind(socketAddress);
118: if (monitorPort == 0) {
119: monitorPort = internalSocket.getLocalPort();
120: log.info("using internal port " + monitorPort);
121: }
122: if (!"mortbay".equals(commKey)) {
123: commKey = Long.toString((long) (Long.MAX_VALUE * Math
124: .random()), 36);
125: log.debug("Using key " + commKey);
126: }
127: } catch (Exception e) {
128: log.fatal(e.getMessage(), e);
129: log.fatal("************ SHUTTING DOWN VM!");
130: System.exit(1);
131: }
132: }
133:
134: /**
135: * Listen to incomming commands until stop command is issued (method
136: * blocks).
137: */
138: private void listen() {
139: boolean goOn = true;
140: while (goOn) {
141: Socket socket = null;
142: try {
143: // wait in blocking mode for an incomming socket connection
144: SocketChannel socketChanel = serverSocketChanel
145: .accept();
146: // we've got a connection here; get concrete socket
147: socket = socketChanel.socket();
148: // attach reader
149: InputStream socketInputStream = socket.getInputStream();
150: InputStreamReader inputStreamReader = new InputStreamReader(
151: socketInputStream);
152: LineNumberReader lnReader = new LineNumberReader(
153: inputStreamReader);
154: String key = lnReader.readLine(); // first line contains auth
155: // key
156: if (!commKey.equals(key)) {
157: log.warn("Keys '" + commKey + "' and '" + key
158: + "' do not match!");
159: continue;
160: }
161:
162: String cmd = lnReader.readLine(); // second line contains
163: // command
164: OutputStream socketOutputStream = socket
165: .getOutputStream();
166: goOn = handleCommand(cmd, socketOutputStream, goOn);
167:
168: } catch (Exception e) {
169: log.error(e.getMessage(), e);
170: } finally {
171: if (socket != null) {
172: try {
173: socket.close();
174: } catch (Exception e) {
175: log.error(e.getMessage());
176: }
177: }
178: socket = null;
179: }
180: }
181: }
182:
183: /**
184: * Handle given command.
185: *
186: * @param cmd
187: * the command to handle
188: * @param socketOutputStream
189: * output stream of socket
190: * @param goOn
191: * current value of goOn
192: * @return value of goOn, possibly changed
193: * @throws IOException
194: */
195: private boolean handleCommand(String cmd,
196: OutputStream socketOutputStream, boolean goOn)
197: throws IOException {
198: log.info("handle command '" + cmd + "'");
199: if ("stop".equals(cmd)) {
200: handleStopCommand(socketOutputStream);
201: goOn = false;
202: } else if ("status".equals(cmd)) {
203: handleStatusCommand(socketOutputStream);
204: }
205: return goOn;
206: }
207:
208: /**
209: * Handle stop command.
210: *
211: * @param socketOutputStream
212: * output stream of socket
213: */
214: private void handleStopCommand(OutputStream socketOutputStream) {
215: // write ack
216: try {
217: log.info("sending reply ACK_STOP");
218: socketOutputStream.write("ACK_STOP\r\n".getBytes());
219: socketOutputStream.flush();
220: } catch (Exception e) {
221: log.error(
222: "sending acknowledgement of stop command failed:",
223: e);
224: }
225: try {
226: server.stop();
227: log.info("Jetty server stopped");
228: } catch (Exception e) {
229: log.fatal(e.getMessage(), e);
230: log
231: .fatal("************* Hard shutdown this server (System.exit)!");
232: System.exit(1);
233: }
234: System.out.flush();
235: System.err.flush();
236: }
237:
238: /**
239: * Handle status command.
240: *
241: * @param socketOutputStream
242: * output stream of socket
243: * @throws IOException
244: */
245: private void handleStatusCommand(OutputStream socketOutputStream)
246: throws IOException {
247: if ((server != null) && server.isStarted()) {
248: log.info("sending reply OK");
249: socketOutputStream.write("OK\r\n".getBytes());
250: } else {
251: if (server == null) {
252: log.info("Server (still) is null");
253: } else {
254: log.info("Server not started yet");
255: }
256: log.info("sending reply STARTING");
257: socketOutputStream.write("STARTING\r\n".getBytes());
258: }
259: socketOutputStream.flush();
260: }
261:
262: /**
263: * Starts a new monitor on the given port, holding the given instance of
264: * Jetty. This static method starts a monitor that listens for admin
265: * requests.
266: *
267: * @param theServer
268: * instance of Jetty Server
269: * @param commKey
270: * auth key
271: * @param monitorPort
272: * port of monitor
273: * @return instance of monitor
274: */
275: public static JettyMonitor startMonitor(Server theServer,
276: String commKey, int monitorPort) {
277: JettyMonitor monitor = new JettyMonitor(commKey, monitorPort);
278: monitor.setServer(theServer);
279: monitor.setDaemon(true);
280: monitor.start();
281: return monitor;
282: }
283:
284: /**
285: * Get server.
286: *
287: * @return Server Returns the server.
288: */
289: public Server getServer() {
290: return server;
291: }
292:
293: /**
294: * Set server.
295: *
296: * @param server
297: * server to set.
298: */
299: public void setServer(Server server) {
300: this.server = server;
301: }
302: }
|