incubator-sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmesc...@apache.org
Subject svn commit: r771647 - in /incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad: app/ControlListener.java app/Main.java base/app/MainDelegate.java
Date Tue, 05 May 2009 09:54:37 GMT
Author: fmeschbe
Date: Tue May  5 09:54:36 2009
New Revision: 771647

URL: http://svn.apache.org/viewvc?rev=771647&view=rev
Log:
SLING-954 Add ControlListener class and start/stop/status support to Main class and add usage
info for new command line arguments
SLING-953 Small fix to command line parsing allowing a single dash as a parameter argument
as in "-f -"

Added:
    incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/ControlListener.java
  (with props)
Modified:
    incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java
    incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/app/MainDelegate.java

Added: incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/ControlListener.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/ControlListener.java?rev=771647&view=auto
==============================================================================
--- incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/ControlListener.java
(added)
+++ incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/ControlListener.java
Tue May  5 09:54:36 2009
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.launchpad.app;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+
+/**
+ * The <code>ControlListener</code> class is a helper class for the {@link Main}
+ * class to support in Sling standalone application process communication. This
+ * class implements the client and server sides of a TCP/IP based communication
+ * channel to control a running Sling application.
+ * <p>
+ * The server side listens for commands on a configurable host and port &endash;
+ * <code>localhost:63000</code> by default &endash; supporting the following
+ * commands:
+ * <table>
+ * <tr>
+ * <th>Command</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td><code>status</code></td>
+ * <td>Request status information. Currently only <i>OK</i> is sent back.
If no
+ * connection can be created to the server the client assumes Sling is not
+ * running.</td>
+ * </tr>
+ * <tr>
+ * <td><code>stop</code></td>
+ * <td>Requests Sling to shutdown.</td>
+ * </tr>
+ * </table>
+ */
+class ControlListener implements Runnable {
+
+    // command sent by the client to cause Sling to shutdown
+    static final String COMMAND_STOP = "stop";
+
+    // command sent by the client to check for the status of the server
+    static final String COMMAND_STATUS = "status";
+
+    // the response sent by the server if the command executed successfully
+    private static final String RESPONSE_OK = "OK";
+
+    // The default port to listen on and to connect to
+    private static final int DEFAULT_LISTEN_PORT = 63000;
+
+    // The reference to the Main class to shutdown on request
+    private final Main slingMain;
+
+    // The socket address used for control communication
+    private final SocketAddress socketAddress;
+
+    /**
+     * Creates an instance of this control support class.
+     * <p>
+     * The host (name or address) and port number of the socket is defined by
+     * the <code>listenSpec</code> parameter. This parameter is defined as
+     * <code>[ host ":" ] port</code>. If the parameter is empty or
+     * <code>null</code> it defaults to <i>localhost:63000</i>. If
the host name
+     * is missing it defaults to <i>localhost</i>.
+     * 
+     * @param slingMain The Main class reference. This is only required if this
+     *            instance is used for the server side to listen for remote stop
+     *            commands. Otherwise this argument may be <code>null</code>.
+     * @param listenSpec The specification for the host and port for the socket
+     *            connection. See above for the format of this parameter.
+     */
+    ControlListener(Main slingMain, String listenSpec) {
+        this.slingMain = slingMain;
+        this.socketAddress = getSocketAddress(listenSpec);
+    }
+
+    /**
+     * Implements the server side of the control connection starting a thread
+     * listening on the host and port configured on setup of this instance.
+     */
+    void listen() {
+        if (socketAddress != null) {
+            Thread listener = new Thread(this);
+            listener.setDaemon(true);
+            listener.setName("Sling Control Listener@" + socketAddress);
+            listener.start();
+        } else {
+            Main.info("No socket address to listen to", null);
+        }
+    }
+
+    /**
+     * Implements the client side of the control connection sending the command
+     * to shutdown Sling.
+     */
+    void shutdownServer() {
+        sendCommand(COMMAND_STOP);
+    }
+
+    /**
+     * Implements the client side of the control connection sending the command
+     * to check whether Sling is active.
+     */
+    void statusServer() {
+        sendCommand(COMMAND_STATUS);
+    }
+
+    // ---------- Runnable interface
+
+    /**
+     * Implements the server thread receiving commands from clients and acting
+     * upon them.
+     */
+    public void run() {
+        ServerSocket server = null;
+        try {
+            server = new ServerSocket();
+            server.bind(socketAddress);
+            Main.info("Sling Control Server started", null);
+        } catch (IOException ioe) {
+            Main.error("Failed to start Sling Control Server", ioe);
+            return;
+        }
+
+        try {
+            while (true) {
+
+                Socket s = server.accept();
+                try {
+                    String command = readLine(s);
+                    Main.info(s.getRemoteSocketAddress() + ">" + command, null);
+
+                    if (COMMAND_STOP.equals(command)) {
+                        slingMain.shutdown();
+                        writeLine(s, RESPONSE_OK);
+
+                        Main.info("Sling shut down, exiting Java VM", null);
+                        System.exit(0);
+
+                    } else if (COMMAND_STATUS.equals(command)) {
+                        writeLine(s, RESPONSE_OK);
+
+                    } else {
+                        writeLine(s, "ERR:" + command);
+
+                    }
+                } finally {
+                    try {
+                        s.close();
+                    } catch (IOException ignore) {
+                    }
+                }
+            }
+        } catch (IOException ioe) {
+            Main.error("Failure reading from client", ioe);
+        } finally {
+            if (server != null) {
+                try {
+                    server.close();
+                } catch (IOException ignore) {
+                }
+            }
+        }
+    }
+
+    // ---------- socket support
+
+    private SocketAddress getSocketAddress(String listenSpec) {
+        try {
+            if (listenSpec != null) {
+                int colon = listenSpec.indexOf(':');
+                if (colon < 0) {
+                    return new InetSocketAddress(InetAddress.getLocalHost(),
+                        Integer.parseInt(listenSpec));
+                }
+                return new InetSocketAddress(listenSpec.substring(0, colon),
+                    Integer.parseInt(listenSpec.substring(colon + 1)));
+            }
+
+            return new InetSocketAddress(InetAddress.getLocalHost(),
+                DEFAULT_LISTEN_PORT);
+        } catch (NumberFormatException nfe) {
+            Main.error("Cannot parse port number from '" + listenSpec + "'",
+                null);
+        } catch (UnknownHostException uhe) {
+            Main.error("Unknown host in '" + listenSpec + "': "
+                + uhe.getMessage(), null);
+        }
+
+        return null;
+    }
+
+    private void sendCommand(String command) {
+        if (socketAddress != null) {
+            Socket socket = null;
+            try {
+                socket = new Socket();
+                socket.connect(socketAddress);
+                writeLine(socket, command);
+                String result = readLine(socket);
+                Main.info("Sent '" + command + "' to " + socketAddress + ": "
+                    + result, null);
+            } catch (ConnectException ce) {
+                Main.info("No Sling running at " + socketAddress, null);
+            } catch (IOException ioe) {
+                Main.error("Failed sending '" + command + "' to "
+                    + socketAddress, ioe);
+            } finally {
+                if (socket != null) {
+                    try {
+                        socket.close();
+                    } catch (IOException ignore) {
+                    }
+                }
+            }
+        } else {
+            Main.info("No socket address to send '" + command + "' to", null);
+        }
+    }
+
+    private String readLine(Socket socket) throws IOException {
+        BufferedReader br = new BufferedReader(new InputStreamReader(
+            socket.getInputStream(), "UTF-8"));
+        return br.readLine();
+    }
+
+    private void writeLine(Socket socket, String line) throws IOException {
+        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
+            socket.getOutputStream(), "UTF-8"));
+        bw.write(line);
+        bw.write("\r\n");
+        bw.flush();
+    }
+}

Propchange: incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/ControlListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/ControlListener.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Modified: incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java?rev=771647&r1=771646&r2=771647&view=diff
==============================================================================
--- incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java
(original)
+++ incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java
Tue May  5 09:54:36 2009
@@ -69,6 +69,9 @@
 
         this.commandLineArgs = parseCommandLine(args);
 
+        // check for control commands (might exit)
+        doControlCommand();
+        
         // sling.home from the command line or system properties, else default
         this.slingHome = getSlingHome(commandLineArgs);
         info("Starting Sling in " + slingHome, null);
@@ -80,6 +83,25 @@
             SharedConstants.DEFAULT_SLING_LAUNCHER_JAR));
     }
 
+    // ---------- Shutdown support for control listener and shutdown hook
+
+    void shutdown() {
+        // remove the shutdown hook, will fail if called from the
+        // shutdown hook itself. Otherwise this prevents shutdown
+        // from being called again
+        try {
+            Runtime.getRuntime().removeShutdownHook(this);
+        } catch (Throwable t) {
+            // don't care for problems removing the hook
+        }
+
+        // now really shutdown sling
+        if (sling != null) {
+            info("Stopping Sling", null);
+            sling.stop();
+        }
+    }
+    
     // ---------- Notifiable interface
 
     /**
@@ -152,10 +174,7 @@
     @Override
     public void run() {
         info("Java VM is shutting down", null);
-        if (sling != null) {
-            info("Stopping Sling", null);
-            sling.stop();
-        }
+        shutdown();
     }
 
     // ---------- internal
@@ -246,7 +265,8 @@
                         commandLine.put(key, arg.substring(2));
                     } else {
                         argc++;
-                        if (argc < args.length && !args[argc].startsWith("-"))
{
+                        if (argc < args.length
+                            && (args[argc].equals("-") || !args[argc].startsWith("-")))
{
                             commandLine.put(key, args[argc]);
                         } else {
                             commandLine.put(key, key);
@@ -312,12 +332,12 @@
     // ---------- logging
 
     // emit an informational message to standard out
-    private static void info(String message, Throwable t) {
+    static void info(String message, Throwable t) {
         log(System.out, "*INFO*", message, t);
     }
 
     // emit an error message to standard err
-    private static void error(String message, Throwable t) {
+    static void error(String message, Throwable t) {
         log(System.err, "*ERROR*", message, t);
     }
 
@@ -353,4 +373,22 @@
             });
         }
     }
+
+    private void doControlCommand() {
+        String commandSocketSpec = commandLineArgs.get("j");
+        if ("j".equals(commandSocketSpec)) {
+            commandSocketSpec = null;
+        }
+
+        ControlListener sl = new ControlListener(this, commandSocketSpec);
+        if (commandLineArgs.remove(ControlListener.COMMAND_STOP) != null) {
+            sl.shutdownServer();
+            System.exit(0);
+        } else if (commandLineArgs.remove(ControlListener.COMMAND_STATUS) != null) {
+            sl.statusServer();
+            System.exit(0);
+        } else if (commandLineArgs.remove("start") != null) {
+            sl.listen();
+        }
+    }
 }

Modified: incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/app/MainDelegate.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/app/MainDelegate.java?rev=771647&r1=771646&r2=771647&view=diff
==============================================================================
--- incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/app/MainDelegate.java
(original)
+++ incubator/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/base/app/MainDelegate.java
Tue May  5 09:54:36 2009
@@ -296,8 +296,12 @@
 
         log("usage: "
             + MainDelegate.class.getName()
-            + " [ -l loglevel ] [ -f logfile ] [ -c slinghome ] [ -a address ] [ -p port
] [ -h ]");
+            + " [ start | stop | status ] [ -j adr ] [ -l loglevel ] [ -f logfile ] [ -c
slinghome ] [ -a address ] [ -p port ] [ -h ]");
 
+        log("    start         listen for control connection (uses -j)");
+        log("    stop          terminate running Sling (uses -j)");
+        log("    start         check whether Sling is running (uses-j)");
+        log("    -j adr        host and port to use for control connection in the format
'[host:]port' (default localhost:63000)");
         log("    -l loglevel   the initial loglevel (0..4, FATAL, ERROR, WARN, INFO, DEBUG)");
         log("    -f logfile    the log file, \"-\" for stdout (default logs/error.log)");
         log("    -c slinghome  the sling context directory (default sling)");



Mime
View raw message