Author: fmeschbe
Date: Tue May 1 15:34:50 2012
New Revision: 1332702
URL: http://svn.apache.org/viewvc?rev=1332702&view=rev
Log:
Add unit tests for ControlListener
Added:
sling/trunk/launchpad/base/src/test/java/org/apache/sling/launchpad/app/ControlListenerTest.java
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=1332702&r1=1332701&r2=1332702&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
Tue May 1 15:34:50 2012
@@ -31,7 +31,7 @@ import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
-import java.net.SocketAddress;
+import java.util.Random;
/**
* The <code>ControlListener</code> class is a helper class for the {@link
Main}
@@ -79,10 +79,10 @@ class ControlListener implements Runnabl
// 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;
+ private final String listenSpec;
- private final boolean writePortConfig;
+ private String secretKey;
+ private InetSocketAddress socketAddress;
/**
* Creates an instance of this control support class.
@@ -98,39 +98,38 @@ 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(final Main slingMain,
- final String listenSpec,
- final boolean selectNewPort) {
+ ControlListener(final Main slingMain, final String listenSpec) {
this.slingMain = slingMain;
- this.socketAddress = this.getSocketAddress(listenSpec, selectNewPort);
- this.writePortConfig = selectNewPort;
+ this.listenSpec = listenSpec; // socketAddress =
this.getSocketAddress(listenSpec, selectNewPort);
}
/**
* 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) {
- final Thread listener = new Thread(this);
- listener.setDaemon(true);
- listener.setName("Apache Sling Control Listener@" + socketAddress);
- listener.start();
- } else {
- Main.info("No socket address to listen to", null);
+ boolean listen() {
+ final File configFile = getConfigFile();
+ if (configFile.canRead() && statusServer() == 0) {
+ // server already running, fail
+ Main.error("Sling already active in " +
this.slingMain.getSlingHome(), null);
+ return false;
}
+ configFile.delete();
+
+ final Thread listener = new Thread(this);
+ listener.setDaemon(true);
+ listener.setName("Apache Sling Control Listener (inactive)");
+ listener.start();
+ return true;
}
/**
* Implements the client side of the control connection sending the command
* to shutdown Sling.
*/
- void shutdownServer() {
- sendCommand(COMMAND_STOP);
+ int shutdownServer() {
+ return sendCommand(COMMAND_STOP);
}
/**
@@ -148,34 +147,71 @@ class ControlListener implements Runnabl
* upon them.
*/
public void run() {
+ this.configure(false);
+
ServerSocket server = null;
try {
server = new ServerSocket();
- server.bind(socketAddress);
- if ( this.writePortConfig ) {
- this.writePortToConfigFile(server);
- }
- Main.info("Apache Sling Control Server started at " +
server.getInetAddress() + ":" + server.getLocalPort(), null);
+ server.bind(this.socketAddress);
+ writePortToConfigFile(getConfigFile(),
+ new InetSocketAddress(server.getInetAddress(),
server.getLocalPort()), this.secretKey);
+ Thread.currentThread().setName(
+ "Apache Sling Control Listener@" + server.getInetAddress() +
":" + server.getLocalPort());
+ Main.info("Apache Sling Control Listener started", null);
} catch (final IOException ioe) {
- Main.error("Failed to start Sling Control Server", ioe);
+ Main.error("Failed to start Apache Sling Control Listener", ioe);
return;
}
+ long delay = 0;
+
try {
while (true) {
final Socket s = server.accept();
+
+ // delay processing after unsuccessfull attempts
+ if (delay > 0) {
+ Main.info(s.getRemoteSocketAddress() + ": Delay: " +
(delay / 1000), null);
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException e) {
+ }
+ }
+
try {
- final String command = readLine(s);
+ final String commandLine = readLine(s);
+ if (commandLine == null) {
+ final String msg = "ERR: missing command";
+ Main.info(s.getRemoteSocketAddress() + "<" + msg,
null);
+ writeLine(s, msg);
+ continue;
+ }
+
+ final int blank = commandLine.indexOf(' ');
+ if (blank < 0) {
+ final String msg = "ERR: missing key";
+ Main.info(s.getRemoteSocketAddress() + "<" + msg,
null);
+ writeLine(s, msg);
+ continue;
+ }
+
+ if (!secretKey.equals(commandLine.substring(0, blank))) {
+ final String msg = "ERR: wrong key";
+ Main.info(s.getRemoteSocketAddress() + "<" + msg,
null);
+ writeLine(s, msg);
+ delay = (delay > 0) ? delay * 2 : 1000L;
+ continue;
+ }
+
+ final String command = commandLine.substring(blank + 1);
Main.info(s.getRemoteSocketAddress() + ">" + command,
null);
if (COMMAND_STOP.equals(command)) {
slingMain.doStop();
Main.info(s.getRemoteSocketAddress() + "<" +
RESPONSE_OK, null);
writeLine(s, RESPONSE_OK);
-
- Main.info("Apache Sling shut down, exiting Java VM",
null);
- System.exit(0);
+ break;
} else if (COMMAND_STATUS.equals(command)) {
Main.info(s.getRemoteSocketAddress() + "<" +
RESPONSE_OK, null);
@@ -202,37 +238,18 @@ class ControlListener implements Runnabl
} catch (final IOException ignore) {
}
}
+
+ getConfigFile().delete();
+
+ // everything has stopped and when this thread terminates,
+ // the VM should stop. If there are still some non-daemon threads
+ // active, this will not happen, so we force this here ...
+ Main.info("Apache Sling terminated, exiting Java VM", null);
+ slingMain.terminateVM(0);
}
// ---------- socket support
- private SocketAddress getSocketAddress(String listenSpec, final boolean
selectNewPort) {
- try {
- if ( listenSpec == null && !selectNewPort ) {
- listenSpec = this.readPortFromConfigFile();
- }
- if ( listenSpec == null ) {
- listenSpec = DEFAULT_LISTEN_INTERFACE + ":" +
DEFAULT_LISTEN_PORT;
- }
- final int colon = listenSpec.indexOf(':');
- final InetSocketAddress addr;
- if (colon < 0) {
- addr = new InetSocketAddress(DEFAULT_LISTEN_INTERFACE,
Integer.parseInt(listenSpec));
- } else {
- addr = new InetSocketAddress(listenSpec.substring(0, colon),
- Integer.parseInt(listenSpec.substring(colon + 1)));
- }
- if (!addr.isUnresolved()) {
- return addr;
- }
- Main.error("Unknown host in '" + listenSpec, null);
- } catch (final NumberFormatException nfe) {
- Main.error("Cannot parse port number from '" + listenSpec + "'",
- null);
- }
-
- return null;
- }
/**
@@ -244,22 +261,25 @@ class ControlListener implements Runnabl
* @return A code indicating success of sending the command.
*/
private int sendCommand(final String command) {
- if (socketAddress != null) {
+ if (configure(true)) {
+ if (this.secretKey == null) {
+ Main.info("Missing secret key to protect sending '" + command
+ "' to " + this.socketAddress, null);
+ return 4; // LSB code for unknown status
+ }
+
Socket socket = null;
try {
socket = new Socket();
- socket.connect(socketAddress);
- writeLine(socket, command);
+ socket.connect(this.socketAddress);
+ writeLine(socket, this.secretKey + " " + command);
final String result = readLine(socket);
- Main.info("Sent '" + command + "' to " + socketAddress + ": "
- + result, null);
+ Main.info("Sent '" + command + "' to " + this.socketAddress +
": " + result, null);
return 0; // LSB code for everything's fine
} catch (final ConnectException ce) {
- Main.info("No Apache Sling running at " + socketAddress, null);
+ Main.info("No Apache Sling running at " + this.socketAddress,
null);
return 3; // LSB code for programm not running
} catch (final IOException ioe) {
- Main.error("Failed sending '" + command + "' to "
- + socketAddress, ioe);
+ Main.error("Failed sending '" + command + "' to " +
this.socketAddress, ioe);
return 1; // LSB code for programm dead
} finally {
if (socket != null) {
@@ -289,6 +309,53 @@ class ControlListener implements Runnabl
}
/**
+ * Read the port from the config file
+ * @return The port or null
+ */
+ private boolean configure(final boolean fromConfigFile) {
+ boolean result = false;
+ if (fromConfigFile) {
+ final File configFile = this.getConfigFile();
+ if (configFile.canRead()) {
+ FileReader fr = null;
+ try {
+ fr = new FileReader(configFile);
+ final LineNumberReader lnr = new LineNumberReader(fr);
+ this.socketAddress = getSocketAddress(lnr.readLine());
+ this.secretKey = lnr.readLine();
+ result = true;
+ } catch (final IOException ignore) {
+ // ignore
+ } finally {
+ if (fr != null) {
+ try {
+ fr.close();
+ } catch (final IOException ignore) {
+ }
+ }
+ }
+ }
+ } else {
+ this.socketAddress = getSocketAddress(this.listenSpec);
+ this.secretKey = generateKey();
+ result = true;
+ }
+
+ return result;
+ }
+
+ private static String generateKey() {
+ String keys =
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789";
+ int len = keys.length();
+ Random r = new Random(System.currentTimeMillis() + 33 *
System.nanoTime());
+ char[] c = new char[32];
+ for (int i = 0; i < c.length; i++) {
+ c[i] = keys.charAt(r.nextInt(len));
+ }
+ return new String(c);
+ }
+
+ /**
* Return the control port file
*/
private File getConfigFile() {
@@ -296,47 +363,60 @@ class ControlListener implements Runnabl
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 ) {}
+ private static InetSocketAddress getSocketAddress(String listenSpec) {
+ try {
+
+ final String address;
+ final int port;
+ if (listenSpec == null) {
+ address = DEFAULT_LISTEN_INTERFACE;
+ port = DEFAULT_LISTEN_PORT;
+ } else {
+ final int colon = listenSpec.indexOf(':');
+ if (colon < 0) {
+ address = DEFAULT_LISTEN_INTERFACE;
+ port = Integer.parseInt(listenSpec);
+ } else {
+ address = listenSpec.substring(0, colon);
+ port = Integer.parseInt(listenSpec.substring(colon + 1));
}
}
+ final InetSocketAddress addr = new InetSocketAddress(address,
port);
+ if (!addr.isUnresolved()) {
+ return addr;
+ }
+
+ Main.error("Unknown host in '" + listenSpec, null);
+ } catch (final NumberFormatException nfe) {
+ Main.error("Cannot parse port number from '" + listenSpec + "'",
+ null);
}
+
return null;
}
- private void writePortToConfigFile(final ServerSocket socket) {
- final File configFile = this.getConfigFile();
+ private static void writePortToConfigFile(final File configFile, final
InetSocketAddress socketAddress,
+ final String secretKey) {
configFile.getParentFile().mkdirs();
FileWriter fw = null;
try {
fw = new FileWriter(configFile);
- fw.write(socket.getInetAddress().getHostName());
+ fw.write(socketAddress.getAddress().getHostAddress());
fw.write(':');
- fw.write(String.valueOf(socket.getLocalPort()));
+ fw.write(String.valueOf(socketAddress.getPort()));
+ fw.write('\n');
+ fw.write(secretKey);
fw.write('\n');
} catch (final IOException ignore) {
// ignore
} finally {
- if ( fw != null ) {
- try { fw.close(); } catch ( final IOException ignore ) {}
+ 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=1332702&r1=1332701&r2=1332702&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
Tue May 1 15:34:50 2012
@@ -116,7 +116,7 @@ public class Main {
// check for control commands
int rc = main.doControlAction();
if (rc >= 0) {
- System.exit(rc);
+ main.terminateVM(rc);
}
// finally start Sling
@@ -242,31 +242,49 @@ public class Main {
* continue as intended, maybe starting the Sling Application. This
* code is returned if the start action (or no action at all) is
* supplied. Otherwise the VM should terminate with the returned
- * code as its exit code. For the stop action, this will be zero.
For
- * the status action, this will be a LSB compliant code for daemon
- * status check: 0 (application running), 1 (Programm Dead), 3
- * (Programm Not Running), 4 (Unknown Problem).
+ * code as its exit code. For the stop action, this will be zero.
+ * For the status action, this will be a LSB compliant code for
+ * daemon status check: 0 (application running), 1 (Programm Dead),
+ * 3 (Programm Not Running), 4 (Unknown Problem).
+ * @see <a
+ *
href="http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html">Init
Script Actions</a>
+ * for a definition of the LSB status codes
*/
protected int doControlAction() {
final ControlAction action = getControlAction();
if (action != null) {
final ControlListener sl = new ControlListener(this,
- commandLineArgs.remove(PROP_CONTROL_SOCKET), action ==
ControlAction.START);
+ commandLineArgs.remove(PROP_CONTROL_SOCKET));
switch (action) {
case START:
- sl.listen();
+ if (!sl.listen()) {
+ // assume service already running
+ return 0;
+ }
break;
case STATUS:
return sl.statusServer();
case STOP:
- sl.shutdownServer();
- return 0;
+ return sl.shutdownServer();
}
}
return -1;
}
+ /**
+ * Terminates the VM which was started by calling the
+ * {@link #main(String[])} method of this class using the
+ * <code>status</code> value as the application exit status code.
+ * <p>
+ * This method does not return.
+ *
+ * @param status The application status exit code.
+ */
+ void terminateVM(final int status) {
+ System.exit(status);
+ }
+
private ControlAction getControlAction() {
Object action = this.commandLineArgs.remove(PROP_CONTROL_ACTION);
if (action != null) {
@@ -434,7 +452,7 @@ public class Main {
* Define the sling.home parameter implementing the algorithme defined on
* the wiki page to find the setting according to this algorithm:
* <ol>
- * <li>Command line option <code>-c</code></li>
+ * <li>Configuration property <code>sling.home</code></li>
* <li>System property <code>sling.home</code></li>
* <li>Environment variable <code>SLING_HOME</code></li>
* <li>Default value <code>sling</code></li>
@@ -629,7 +647,7 @@ public class Main {
System.out.println(" start listen for control
connection (uses -j)");
System.out.println(" stop terminate running Apache
Sling (uses -j)");
System.out.println(" status check whether Apache Sling
is running (uses -j)");
- System.out.println(" -j adr host and port to use for
control connection in the format '[host:]port' (default localhost:63000)");
+ System.out.println(" -j adr host and port to use for
control connection in the format '[host:]port' (default 127.0.0.1:0)");
System.out.println(" -l loglevel the initial loglevel (0..4,
FATAL, ERROR, WARN, INFO, DEBUG)");
System.out.println(" -f logfile the log file, \"-\" for
stdout (default logs/error.log)");
System.out.println(" -c slinghome the sling context directory
(default sling)");
Added:
sling/trunk/launchpad/base/src/test/java/org/apache/sling/launchpad/app/ControlListenerTest.java
URL:
http://svn.apache.org/viewvc/sling/trunk/launchpad/base/src/test/java/org/apache/sling/launchpad/app/ControlListenerTest.java?rev=1332702&view=auto
==============================================================================
---
sling/trunk/launchpad/base/src/test/java/org/apache/sling/launchpad/app/ControlListenerTest.java
(added)
+++
sling/trunk/launchpad/base/src/test/java/org/apache/sling/launchpad/app/ControlListenerTest.java
Tue May 1 15:34:50 2012
@@ -0,0 +1,246 @@
+/*
+ * 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.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.ServerSocket;
+import java.util.HashMap;
+
+import junit.framework.TestCase;
+
+import org.apache.sling.launchpad.base.shared.SharedConstants;
+
+public class ControlListenerTest extends TestCase {
+
+ private static String SLING1 = "target/sling.test/1";
+
+ private static String SLING2 = "target/sling.test/2";
+
+ private static String CTL = "conf/controlport";
+
+ private final File ctlFile1 = new File(SLING1, CTL);
+
+ private final File ctlFile2 = new File(SLING2, CTL);
+
+ @Override
+ protected void tearDown() throws Exception {
+ ctlFile1.delete();
+ ctlFile2.delete();
+
+ super.tearDown();
+ }
+
+ public void test_start_status_stop() {
+ int port = getPort();
+ MyMain main = new MyMain(SLING1);
+
+ ControlListener cl = new ControlListener(main, String.valueOf(port));
+ TestCase.assertTrue(cl.listen());
+ delay(); // wait for sever to start
+
+ TestCase.assertTrue(ctlFile1.canRead());
+
+ TestCase.assertEquals(0, new ControlListener(main,
null).statusServer());
+
+ TestCase.assertEquals(0, new ControlListener(main,
null).shutdownServer());
+
+ TestCase.assertTrue(main.stopCalled);
+
+ delay();
+ TestCase.assertFalse(ctlFile1.exists());
+ }
+
+ public void test_parallel_start_status_stop() {
+ int port1 = getPort();
+ MyMain main1 = new MyMain(SLING1);
+ ControlListener server1 = new ControlListener(main1,
String.valueOf(port1));
+ TestCase.assertTrue(server1.listen());
+ delay(); // wait for sever to start
+
+ int port2 = getPort();
+ MyMain main2 = new MyMain(SLING2);
+ ControlListener server2 = new ControlListener(main2,
String.valueOf(port2));
+ TestCase.assertTrue(server2.listen());
+ delay(); // wait for sever to start
+
+ TestCase.assertTrue(ctlFile1.canRead());
+ TestCase.assertTrue(ctlFile2.canRead());
+
+ TestCase.assertEquals(0, new ControlListener(main1,
null).statusServer());
+ TestCase.assertEquals(0, new ControlListener(main1,
null).shutdownServer());
+
+ TestCase.assertEquals(0, new ControlListener(main2,
null).statusServer());
+ TestCase.assertEquals(0, new ControlListener(main2,
null).shutdownServer());
+
+ TestCase.assertTrue(main1.stopCalled);
+ TestCase.assertTrue(main2.stopCalled);
+
+ delay();
+ TestCase.assertFalse(ctlFile1.exists());
+ TestCase.assertFalse(ctlFile2.exists());
+ }
+
+ public void test_no_start_in_same_sling_home() {
+ int port1 = getPort();
+ MyMain main1 = new MyMain(SLING1);
+ ControlListener server1 = new ControlListener(main1,
String.valueOf(port1));
+ TestCase.assertTrue(server1.listen());
+ delay(); // wait for sever to start
+
+ int port2 = getPort();
+ MyMain main2 = new MyMain(SLING1);
+ ControlListener server2 = new ControlListener(main2,
String.valueOf(port2));
+ TestCase.assertFalse(server2.listen());
+ delay(); // wait for sever to start
+
+ TestCase.assertTrue(ctlFile1.canRead());
+ TestCase.assertFalse(ctlFile2.canRead());
+
+ TestCase.assertEquals(0, new ControlListener(main1,
null).statusServer());
+ TestCase.assertEquals(0, new ControlListener(main1,
null).shutdownServer());
+
+ TestCase.assertTrue(main1.stopCalled);
+
+ delay();
+ TestCase.assertFalse(ctlFile1.exists());
+
+ // retry cl2
+ TestCase.assertTrue(server2.listen());
+ delay(); // wait for sever to start
+
+ TestCase.assertEquals(0, new ControlListener(main2,
null).statusServer());
+ TestCase.assertEquals(0, new ControlListener(main2,
null).shutdownServer());
+
+ TestCase.assertTrue(main2.stopCalled);
+
+ delay();
+ TestCase.assertFalse(ctlFile2.exists());
+ }
+
+ public void test_no_status() throws IOException {
+ int port = getPort();
+ MyMain main = new MyMain(SLING1);
+
+ TestCase.assertFalse(ctlFile1.exists());
+ ctlFile1.getParentFile().mkdirs();
+ FileOutputStream out = new FileOutputStream(ctlFile1);
+ PrintWriter pw = new PrintWriter(out);
+ pw.println("127.0.0.1:" + port);
+ pw.println("password");
+ pw.close();
+
+ TestCase.assertTrue(new File(SLING1, CTL).canRead());
+
+ TestCase.assertEquals(3, new ControlListener(main,
null).statusServer());
+
+ TestCase.assertEquals(3, new ControlListener(main,
null).shutdownServer());
+
+ TestCase.assertFalse(main.stopCalled);
+
+ TestCase.assertTrue(ctlFile1.exists());
+ }
+
+ public void test_no_configuration_for_status() {
+ int port = getPort();
+ MyMain main = new MyMain(SLING1);
+
+ TestCase.assertFalse(ctlFile1.exists());
+
+ TestCase.assertEquals(4, new ControlListener(main,
String.valueOf(port)).statusServer());
+
+ TestCase.assertEquals(4, new ControlListener(main,
String.valueOf(port)).shutdownServer());
+
+ TestCase.assertFalse(main.stopCalled);
+
+ TestCase.assertFalse(ctlFile1.exists());
+ }
+
+ public void test_no_key_for_status() throws IOException {
+ int port = getPort();
+ MyMain main = new MyMain(SLING1);
+
+ TestCase.assertFalse(ctlFile1.exists());
+ ctlFile1.getParentFile().mkdirs();
+ FileOutputStream out = new FileOutputStream(ctlFile1);
+ PrintWriter pw = new PrintWriter(out);
+ pw.println("127.0.0.1:" + port);
+ pw.close();
+
+ TestCase.assertTrue(new File(SLING1, CTL).canRead());
+
+ TestCase.assertEquals(4, new ControlListener(main,
null).statusServer());
+
+ TestCase.assertEquals(4, new ControlListener(main,
null).shutdownServer());
+
+ TestCase.assertFalse(main.stopCalled);
+
+ TestCase.assertTrue(ctlFile1.exists());
+ }
+
+ private int getPort() {
+ ServerSocket s = null;
+ try {
+ s = new ServerSocket(0);
+ return s.getLocalPort();
+ } catch (IOException ignore) {
+ } finally {
+ if (s != null) {
+ try {
+ s.close();
+ } catch (IOException i2) {
+ }
+ }
+ }
+
+ TestCase.fail("Cannot acquire port");
+ return 0; // compiler satisfaction
+ }
+
+ private void delay() {
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException ie) {
+ }
+ }
+
+ private static class MyMain extends Main {
+
+ boolean stopCalled;
+
+ MyMain(final String slingHome) {
+ super(new HashMap<String, String>() {
+ {
+ put(SharedConstants.SLING_HOME, slingHome);
+ }
+ });
+ }
+
+ protected void doStop() {
+ this.stopCalled = true;
+ }
+
+ @Override
+ void terminateVM(int status) {
+ // not really
+ }
+ }
+}