You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2012/01/05 11:08:01 UTC

svn commit: r1227536 - in /sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app: ControlListener.java Main.java

Author: cziegeler
Date: Thu Jan  5 10:08:01 2012
New Revision: 1227536

URL: http://svn.apache.org/viewvc?rev=1227536&view=rev
Log:
SLING-2345 : Improve control port handling

Modified:
    sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/ControlListener.java
    sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java

Modified: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/ControlListener.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/ControlListener.java?rev=1227536&r1=1227535&r2=1227536&view=diff
==============================================================================
--- sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/ControlListener.java (original)
+++ sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/ControlListener.java Thu Jan  5 10:08:01 2012
@@ -20,8 +20,12 @@ package org.apache.sling.launchpad.app;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.io.LineNumberReader;
 import java.io.OutputStreamWriter;
 import java.net.ConnectException;
 import java.net.InetAddress;
@@ -68,8 +72,8 @@ class ControlListener implements Runnabl
     // 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 default port to listen on and to connect to - we select it randomly
+    private static final int DEFAULT_LISTEN_PORT = 0;
 
     // The reference to the Main class to shutdown on request
     private final Main slingMain;
@@ -77,13 +81,15 @@ class ControlListener implements Runnabl
     // The socket address used for control communication
     private final SocketAddress socketAddress;
 
+    private final boolean writePortConfig;
+
     /**
      * 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
+     * <code>null</code> it defaults to <i>localhost:0</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
@@ -91,10 +97,16 @@ class ControlListener implements Runnabl
      *            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.
+     * @param selectNewPort Parameter specifying if a new port should be selected
+     *                      or if no port is specified a stored port should be
+     *                      used.
      */
-    ControlListener(Main slingMain, String listenSpec) {
+    ControlListener(final Main slingMain,
+                    final String listenSpec,
+                    final boolean selectNewPort) {
         this.slingMain = slingMain;
-        this.socketAddress = getSocketAddress(listenSpec);
+        this.socketAddress = this.getSocketAddress(listenSpec, selectNewPort);
+        this.writePortConfig = selectNewPort;
     }
 
     /**
@@ -103,7 +115,7 @@ class ControlListener implements Runnabl
      */
     void listen() {
         if (socketAddress != null) {
-            Thread listener = new Thread(this);
+            final Thread listener = new Thread(this);
             listener.setDaemon(true);
             listener.setName("Apache Sling Control Listener@" + socketAddress);
             listener.start();
@@ -139,8 +151,11 @@ class ControlListener implements Runnabl
         try {
             server = new ServerSocket();
             server.bind(socketAddress);
-            Main.info("Apache Sling Control Server started", null);
-        } catch (IOException ioe) {
+            if ( this.writePortConfig ) {
+                this.writePortToConfigFile(server);
+            }
+            Main.info("Apache Sling Control Server started at " + server.getInetAddress() + ":" + server.getLocalPort(), null);
+        } catch (final IOException ioe) {
             Main.error("Failed to start Sling Control Server", ioe);
             return;
         }
@@ -148,9 +163,9 @@ class ControlListener implements Runnabl
         try {
             while (true) {
 
-                Socket s = server.accept();
+                final Socket s = server.accept();
                 try {
-                    String command = readLine(s);
+                    final String command = readLine(s);
                     Main.info(s.getRemoteSocketAddress() + ">" + command, null);
 
                     if (COMMAND_STOP.equals(command)) {
@@ -174,36 +189,37 @@ class ControlListener implements Runnabl
                     }
                 }
             }
-        } catch (IOException ioe) {
+        } catch (final IOException ioe) {
             Main.error("Failure reading from client", ioe);
         } finally {
             try {
                 server.close();
-            } catch (IOException ignore) {
+            } catch (final IOException ignore) {
             }
         }
     }
 
     // ---------- socket support
 
-    private SocketAddress getSocketAddress(String listenSpec) {
+    private SocketAddress getSocketAddress(String listenSpec, final boolean selectNewPort) {
         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)));
+            if ( listenSpec == null && !selectNewPort ) {
+                listenSpec = this.readPortFromConfigFile();
             }
-
-            return new InetSocketAddress(InetAddress.getLocalHost(),
-                DEFAULT_LISTEN_PORT);
-        } catch (NumberFormatException nfe) {
+            if ( listenSpec == null ) {
+                listenSpec = InetAddress.getLocalHost().getHostName() + ':' + String.valueOf(DEFAULT_LISTEN_PORT);
+            }
+            final 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)));
+        } catch (final NumberFormatException nfe) {
             Main.error("Cannot parse port number from '" + listenSpec + "'",
                 null);
-        } catch (UnknownHostException uhe) {
+        } catch (final UnknownHostException uhe) {
             Main.error("Unknown host in '" + listenSpec + "': "
                 + uhe.getMessage(), null);
         }
@@ -219,21 +235,21 @@ class ControlListener implements Runnabl
      *
      * @return A code indicating success of sending the command.
      */
-    private int sendCommand(String command) {
+    private int sendCommand(final String command) {
         if (socketAddress != null) {
             Socket socket = null;
             try {
                 socket = new Socket();
                 socket.connect(socketAddress);
                 writeLine(socket, command);
-                String result = readLine(socket);
+                final String result = readLine(socket);
                 Main.info("Sent '" + command + "' to " + socketAddress + ": "
                     + result, null);
                 return 0; // LSB code for everything's fine
-            } catch (ConnectException ce) {
+            } catch (final ConnectException ce) {
                 Main.info("No Apache Sling running at " + socketAddress, null);
                 return 3; // LSB code for programm not running
-            } catch (IOException ioe) {
+            } catch (final IOException ioe) {
                 Main.error("Failed sending '" + command + "' to "
                     + socketAddress, ioe);
                 return 1; // LSB code for programm dead
@@ -245,23 +261,74 @@ class ControlListener implements Runnabl
                     }
                 }
             }
-        } else {
-            Main.info("No socket address to send '" + command + "' to", null);
-            return 4; // LSB code for unknown status
         }
+        Main.info("No socket address to send '" + command + "' to", null);
+        return 4; // LSB code for unknown status
     }
 
-    private String readLine(Socket socket) throws IOException {
-        BufferedReader br = new BufferedReader(new InputStreamReader(
+    private String readLine(final Socket socket) throws IOException {
+        final 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(
+    private void writeLine(final Socket socket, final String line) throws IOException {
+        final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
             socket.getOutputStream(), "UTF-8"));
         bw.write(line);
         bw.write("\r\n");
         bw.flush();
     }
+
+    /**
+     * Return the control port file
+     */
+    private File getConfigFile() {
+        final File configDir = new File(this.slingMain.getSlingHome(), "conf");
+        return new File(configDir, "controlport");
+    }
+
+    /**
+     * Read the port from the config file
+     * @return The port or null
+     */
+    private String readPortFromConfigFile() {
+        final File configFile = this.getConfigFile();
+        if ( configFile.canRead() ) {
+            FileReader fr = null;
+            try {
+                fr = new FileReader(configFile);
+                final LineNumberReader lnr = new LineNumberReader(fr);
+                return lnr.readLine();
+            } catch (final IOException ignore) {
+                // ignore
+            } finally {
+                if ( fr != null ) {
+                    try { fr.close(); } catch ( final IOException ignore ) {}
+                }
+            }
+
+        }
+        return null;
+    }
+
+    private void writePortToConfigFile(final ServerSocket socket) {
+        final File configFile = this.getConfigFile();
+        configFile.getParentFile().mkdirs();
+        FileWriter fw = null;
+        try {
+            fw = new FileWriter(configFile);
+            fw.write(socket.getInetAddress().getHostName());
+            fw.write(':');
+            fw.write(String.valueOf(socket.getLocalPort()));
+            fw.write('\n');
+        } catch (final IOException ignore) {
+            // ignore
+        } finally {
+            if ( fw != null ) {
+                try { fw.close(); } catch ( final IOException ignore ) {}
+            }
+        }
+
+    }
 }

Modified: sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java?rev=1227536&r1=1227535&r2=1227536&view=diff
==============================================================================
--- sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java (original)
+++ sling/trunk/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java Thu Jan  5 10:08:01 2012
@@ -87,7 +87,7 @@ public class Main {
      * @param args The command line arguments supplied when starting the Sling
      *            Launcher through the Java VM.
      */
-    public static void main(String[] args) {
+    public static void main(final String[] args) {
         final Map<String, String> rawArgs = parseCommandLine(args);
 
         // support usage first
@@ -100,7 +100,7 @@ public class Main {
             System.exit(1);
         }
 
-        Main main = new Main(props);
+        final Main main = new Main(props);
 
         // check for control commands
         int rc = main.doControlAction();
@@ -136,7 +136,7 @@ public class Main {
      *
      * @see #getSlingHome(Map)
      */
-    private String slingHome;
+    private final String slingHome;
 
     /**
      * The {@link Loader} class used to create the Framework class loader and
@@ -151,6 +151,11 @@ public class Main {
     private Launcher sling;
 
     /**
+     * Flag to indicate if Sling has already been started.
+     */
+    private boolean started = false;
+
+    /**
      * Creates an instance of this main loader class. The provided arguments are
      * used to configure the OSGi framework being launched with the
      * {@link #startSling(URL)} method.
@@ -165,6 +170,13 @@ public class Main {
         this.commandLineArgs = (args == null)
                 ? new HashMap<String, String>()
                 : args;
+        // sling.home from the command line or system properties, else default
+        String home = getSlingHome(commandLineArgs);
+        final File slingHomeFile = new File(home);
+        if (!slingHomeFile.isAbsolute()) {
+            home = slingHomeFile.getAbsolutePath();
+        }
+        this.slingHome = home;
     }
 
     /**
@@ -225,10 +237,10 @@ public class Main {
      *         (Programm Not Running), 4 (Unknown Problem).
      */
     protected int doControlAction() {
-        ControlAction action = getControlAction();
+        final ControlAction action = getControlAction();
         if (action != null) {
-            ControlListener sl = new ControlListener(this,
-                commandLineArgs.remove(PROP_CONTROL_SOCKET));
+            final ControlListener sl = new ControlListener(this,
+                commandLineArgs.remove(PROP_CONTROL_SOCKET), action == ControlAction.START);
             switch (action) {
                 case START:
                     sl.listen();
@@ -238,8 +250,6 @@ public class Main {
                 case STOP:
                     sl.shutdownServer();
                     return 0;
-                default:
-                    error("Unsupported control action: " + action, null);
             }
         }
 
@@ -276,19 +286,13 @@ public class Main {
     protected boolean doStart() {
 
         // prevent duplicate start
-        if (this.slingHome != null) {
+        if ( this.started) {
             info("Apache Sling has already been started", null);
             return true;
         }
 
-        // sling.home from the command line or system properties, else default
-        String slingHome = getSlingHome(commandLineArgs);
-        File slingHomeFile = new File(slingHome);
-        if (!slingHomeFile.isAbsolute()) {
-            slingHome = slingHomeFile.getAbsolutePath();
-        }
         info("Starting Apache Sling in " + slingHome, null);
-        this.slingHome = slingHome;
+        this.started = true;
 
         // The Loader helper
         Loader loaderTmp = null;
@@ -412,7 +416,7 @@ public class Main {
         }
 
         // further cleanup
-        this.slingHome = null;
+        this.started = false;
     }
 
     /**
@@ -464,6 +468,13 @@ public class Main {
     }
 
     /**
+     * Return the absolute path to sling home
+     */
+    public String getSlingHome() {
+        return this.slingHome;
+    }
+
+    /**
      * Define the sling.launchpad parameter implementing the algorithme defined
      * on the wiki page to find the setting according to this algorithm:
      * <ol>
@@ -480,7 +491,7 @@ public class Main {
      */
     private static File getLaunchpadHome(final String slingHome,
             final Map<String, String> commandLineArgs) {
-        String launchpadHomeParam = commandLineArgs.get(SharedConstants.SLING_LAUNCHPAD);
+        final String launchpadHomeParam = commandLineArgs.get(SharedConstants.SLING_LAUNCHPAD);
         if (launchpadHomeParam == null || launchpadHomeParam.length() == 0) {
             commandLineArgs.put(SharedConstants.SLING_LAUNCHPAD, slingHome);
             return new File(slingHome);