Save This Page
Home » org.apache.sling.launchpad.base-2.2.0-source-release » org.apache.sling.launchpad.app » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    *   http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.
   18    */
   19   package org.apache.sling.launchpad.app;
   20   
   21   import java.io.BufferedReader;
   22   import java.io.BufferedWriter;
   23   import java.io.IOException;
   24   import java.io.InputStreamReader;
   25   import java.io.OutputStreamWriter;
   26   import java.net.ConnectException;
   27   import java.net.InetAddress;
   28   import java.net.InetSocketAddress;
   29   import java.net.ServerSocket;
   30   import java.net.Socket;
   31   import java.net.SocketAddress;
   32   import java.net.UnknownHostException;
   33   
   34   /**
   35    * The <code>ControlListener</code> class is a helper class for the {@link Main}
   36    * class to support in Sling standalone application process communication. This
   37    * class implements the client and server sides of a TCP/IP based communication
   38    * channel to control a running Sling application.
   39    * <p>
   40    * The server side listens for commands on a configurable host and port &endash;
   41    * <code>localhost:63000</code> by default &endash; supporting the following
   42    * commands:
   43    * <table>
   44    * <tr>
   45    * <th>Command</th>
   46    * <th>Description</th>
   47    * </tr>
   48    * <tr>
   49    * <td><code>status</code></td>
   50    * <td>Request status information. Currently only <i>OK</i> is sent back. If no
   51    * connection can be created to the server the client assumes Sling is not
   52    * running.</td>
   53    * </tr>
   54    * <tr>
   55    * <td><code>stop</code></td>
   56    * <td>Requests Sling to shutdown.</td>
   57    * </tr>
   58    * </table>
   59    */
   60   class ControlListener implements Runnable {
   61   
   62       // command sent by the client to cause Sling to shutdown
   63       static final String COMMAND_STOP = "stop";
   64   
   65       // command sent by the client to check for the status of the server
   66       static final String COMMAND_STATUS = "status";
   67   
   68       // the response sent by the server if the command executed successfully
   69       private static final String RESPONSE_OK = "OK";
   70   
   71       // The default port to listen on and to connect to
   72       private static final int DEFAULT_LISTEN_PORT = 63000;
   73   
   74       // The reference to the Main class to shutdown on request
   75       private final Main slingMain;
   76   
   77       // The socket address used for control communication
   78       private final SocketAddress socketAddress;
   79   
   80       /**
   81        * Creates an instance of this control support class.
   82        * <p>
   83        * The host (name or address) and port number of the socket is defined by
   84        * the <code>listenSpec</code> parameter. This parameter is defined as
   85        * <code>[ host ":" ] port</code>. If the parameter is empty or
   86        * <code>null</code> it defaults to <i>localhost:63000</i>. If the host name
   87        * is missing it defaults to <i>localhost</i>.
   88        *
   89        * @param slingMain The Main class reference. This is only required if this
   90        *            instance is used for the server side to listen for remote stop
   91        *            commands. Otherwise this argument may be <code>null</code>.
   92        * @param listenSpec The specification for the host and port for the socket
   93        *            connection. See above for the format of this parameter.
   94        */
   95       ControlListener(Main slingMain, String listenSpec) {
   96           this.slingMain = slingMain;
   97           this.socketAddress = getSocketAddress(listenSpec);
   98       }
   99   
  100       /**
  101        * Implements the server side of the control connection starting a thread
  102        * listening on the host and port configured on setup of this instance.
  103        */
  104       void listen() {
  105           if (socketAddress != null) {
  106               Thread listener = new Thread(this);
  107               listener.setDaemon(true);
  108               listener.setName("Sling Control Listener@" + socketAddress);
  109               listener.start();
  110           } else {
  111               Main.info("No socket address to listen to", null);
  112           }
  113       }
  114   
  115       /**
  116        * Implements the client side of the control connection sending the command
  117        * to shutdown Sling.
  118        */
  119       void shutdownServer() {
  120           sendCommand(COMMAND_STOP);
  121       }
  122   
  123       /**
  124        * Implements the client side of the control connection sending the command
  125        * to check whether Sling is active.
  126        */
  127       void statusServer() {
  128           sendCommand(COMMAND_STATUS);
  129       }
  130   
  131       // ---------- Runnable interface
  132   
  133       /**
  134        * Implements the server thread receiving commands from clients and acting
  135        * upon them.
  136        */
  137       public void run() {
  138           ServerSocket server = null;
  139           try {
  140               server = new ServerSocket();
  141               server.bind(socketAddress);
  142               Main.info("Sling Control Server started", null);
  143           } catch (IOException ioe) {
  144               Main.error("Failed to start Sling Control Server", ioe);
  145               return;
  146           }
  147   
  148           try {
  149               while (true) {
  150   
  151                   Socket s = server.accept();
  152                   try {
  153                       String command = readLine(s);
  154                       Main.info(s.getRemoteSocketAddress() + ">" + command, null);
  155   
  156                       if (COMMAND_STOP.equals(command)) {
  157                           slingMain.shutdown();
  158                           writeLine(s, RESPONSE_OK);
  159   
  160                           Main.info("Sling shut down, exiting Java VM", null);
  161                           System.exit(0);
  162   
  163                       } else if (COMMAND_STATUS.equals(command)) {
  164                           writeLine(s, RESPONSE_OK);
  165   
  166                       } else {
  167                           writeLine(s, "ERR:" + command);
  168   
  169                       }
  170                   } finally {
  171                       try {
  172                           s.close();
  173                       } catch (IOException ignore) {
  174                       }
  175                   }
  176               }
  177           } catch (IOException ioe) {
  178               Main.error("Failure reading from client", ioe);
  179           } finally {
  180               try {
  181                   server.close();
  182               } catch (IOException ignore) {
  183               }
  184           }
  185       }
  186   
  187       // ---------- socket support
  188   
  189       private SocketAddress getSocketAddress(String listenSpec) {
  190           try {
  191               if (listenSpec != null) {
  192                   int colon = listenSpec.indexOf(':');
  193                   if (colon < 0) {
  194                       return new InetSocketAddress(InetAddress.getLocalHost(),
  195                           Integer.parseInt(listenSpec));
  196                   }
  197                   return new InetSocketAddress(listenSpec.substring(0, colon),
  198                       Integer.parseInt(listenSpec.substring(colon + 1)));
  199               }
  200   
  201               return new InetSocketAddress(InetAddress.getLocalHost(),
  202                   DEFAULT_LISTEN_PORT);
  203           } catch (NumberFormatException nfe) {
  204               Main.error("Cannot parse port number from '" + listenSpec + "'",
  205                   null);
  206           } catch (UnknownHostException uhe) {
  207               Main.error("Unknown host in '" + listenSpec + "': "
  208                   + uhe.getMessage(), null);
  209           }
  210   
  211           return null;
  212       }
  213   
  214       private void sendCommand(String command) {
  215           if (socketAddress != null) {
  216               Socket socket = null;
  217               try {
  218                   socket = new Socket();
  219                   socket.connect(socketAddress);
  220                   writeLine(socket, command);
  221                   String result = readLine(socket);
  222                   Main.info("Sent '" + command + "' to " + socketAddress + ": "
  223                       + result, null);
  224               } catch (ConnectException ce) {
  225                   Main.info("No Sling running at " + socketAddress, null);
  226               } catch (IOException ioe) {
  227                   Main.error("Failed sending '" + command + "' to "
  228                       + socketAddress, ioe);
  229               } finally {
  230                   if (socket != null) {
  231                       try {
  232                           socket.close();
  233                       } catch (IOException ignore) {
  234                       }
  235                   }
  236               }
  237           } else {
  238               Main.info("No socket address to send '" + command + "' to", null);
  239           }
  240       }
  241   
  242       private String readLine(Socket socket) throws IOException {
  243           BufferedReader br = new BufferedReader(new InputStreamReader(
  244               socket.getInputStream(), "UTF-8"));
  245           return br.readLine();
  246       }
  247   
  248       private void writeLine(Socket socket, String line) throws IOException {
  249           BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
  250               socket.getOutputStream(), "UTF-8"));
  251           bw.write(line);
  252           bw.write("\r\n");
  253           bw.flush();
  254       }
  255   }

Save This Page
Home » org.apache.sling.launchpad.base-2.2.0-source-release » org.apache.sling.launchpad.app » [javadoc | source]