public void recycle();
}
+ protected enum BindState {
+ UNBOUND, BOUND_ON_INIT, BOUND_ON_START
+ }
+
// Standard SSL Configuration attributes
// JSSE
// Standard configuration attribute names
protected volatile boolean paused = false;
/**
- * Track the initialization state of the endpoint.
- */
- protected boolean initialized = false;
-
- /**
* Are we using an internal executor
*/
protected volatile boolean internalExecutor = false;
public int getBacklog() { return backlog; }
/**
+ * Controls when the Endpoint binds the port. <code>true</code>, the default
+ * binds the port on {@link #init()} and unbinds it on {@link #destroy()}.
+ * If set to <code>false</code> the port is bound on {@link #start()} and
+ * unbound on {@link #stop()}.
+ */
+ private boolean bindOnInit = true;
+ public boolean getBindOnInit() { return bindOnInit; }
+ public void setBindOnInit(boolean b) { this.bindOnInit = b; }
+ private BindState bindState = BindState.UNBOUND;
+
+ /**
* Keepalive timeout, if lesser or equal to 0 then soTimeout will be used.
*/
private int keepAliveTimeout = -1;
}
- public abstract void init() throws Exception;
- public abstract void start() throws Exception;
+ // ------------------------------------------------------- Lifecycle methods
+
+ /*
+ * NOTE: There is no maintenance of state or checking for valid transitions
+ * within this class other than ensuring that bind/unbind are called in the
+ * right place. It is expected that the calling code will maintain state and
+ * prevent invalid state transitions.
+ */
+
+ public abstract void bind() throws Exception;
+ public abstract void unbind() throws Exception;
+ public abstract void startInternal() throws Exception;
+ public abstract void stopInternal() throws Exception;
+
+ public final void init() throws Exception {
+ if (bindOnInit) {
+ bind();
+ bindState = BindState.BOUND_ON_INIT;
+ }
+ }
+
+ public final void start() throws Exception {
+ if (bindState == BindState.UNBOUND) {
+ bind();
+ bindState = BindState.BOUND_ON_START;
+ }
+ startInternal();
+ }
/**
* Pause the endpoint, which will stop it accepting new connections.
}
}
- public abstract void stop() throws Exception;
- public abstract void destroy() throws Exception;
+ public final void stop() throws Exception {
+ stopInternal();
+ if (bindState == BindState.BOUND_ON_START) {
+ unbind();
+ bindState = BindState.UNBOUND;
+ }
+ }
+
+ public final void destroy() throws Exception {
+ if (bindState == BindState.BOUND_ON_INIT) {
+ unbind();
+ bindState = BindState.UNBOUND;
+ }
+ }
+
public String adjustRelativePath(String path, String relativeTo) {
String newPath = path;
* Initialize the endpoint.
*/
@Override
- public void init()
- throws Exception {
-
- if (initialized)
- return;
+ public void bind() throws Exception {
// Create the root APR memory pool
try {
// For now, sendfile is not supported with SSL
useSendfile = false;
}
-
- initialized = true;
-
}
* Start the APR endpoint, creating acceptor, poller and sendfile threads.
*/
@Override
- public void start()
- throws Exception {
- // Initialize socket if not done before
- if (!initialized) {
- init();
- }
+ public void startInternal() throws Exception {
+
if (!running) {
running = true;
paused = false;
* Stop the endpoint. This will cause all processing threads to stop.
*/
@Override
- public void stop() {
+ public void stopInternal() {
if (!paused) {
pause();
}
* Deallocate APR memory pools, and close server socket.
*/
@Override
- public void destroy() throws Exception {
+ public void unbind() throws Exception {
if (running) {
stop();
}
}
handler.recycle();
-
- initialized = false;
}
// -------------------- Public methods --------------------
@Override
- public void init()
- throws Exception {
+ public void bind() throws Exception {
- if (initialized)
- return;
-
// Initialize thread count defaults for acceptor
if (acceptorThreadCount == 0) {
acceptorThreadCount = 1;
}
}
- initialized = true;
}
@Override
- public void start() throws Exception {
- // Initialize socket if not done before
- if (!initialized) {
- init();
- }
+ public void startInternal() throws Exception {
+
if (!running) {
running = true;
paused = false;
}
@Override
- public void stop() {
+ public void stopInternal() {
if (!paused) {
pause();
}
* Deallocate APR memory pools, and close server socket.
*/
@Override
- public void destroy() throws Exception {
+ public void unbind() throws Exception {
if (running) {
stop();
}
serverSocket = null;
}
handler.recycle();
- initialized = false ;
}
* Initialize the endpoint.
*/
@Override
- public void init()
- throws Exception {
-
- if (initialized)
- return;
+ public void bind() throws Exception {
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
if (oomParachute>0) reclaimParachute(true);
selectorPool.open();
- initialized = true;
-
}
public KeyManager[] wrap(KeyManager[] managers) {
* Start the NIO endpoint, creating acceptor, poller threads.
*/
@Override
- public void start()
- throws Exception {
- // Initialize socket if not done before
- if (!initialized) {
- init();
- }
+ public void startInternal() throws Exception {
+
if (!running) {
running = true;
paused = false;
* Stop the endpoint. This will cause all processing threads to stop.
*/
@Override
- public void stop() {
+ public void stopInternal() {
if (!paused) {
pause();
}
* Deallocate NIO memory pools, and close server socket.
*/
@Override
- public void destroy() throws Exception {
+ public void unbind() throws Exception {
if (log.isDebugEnabled()) {
log.debug("Destroy initiated for "+new InetSocketAddress(getAddress(),getPort()));
}
serverSock.close();
serverSock = null;
sslContext = null;
- initialized = false;
releaseCaches();
selectorPool.close();
if (log.isDebugEnabled()) {
import java.io.File;
import java.net.ServerSocket;
+import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
*/
public class TestXxxEndpoint extends TomcatBaseTest {
- public void disbaletestStartStop() throws Exception {
+ public void testStartStopBindOnInit() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File(getBuildDirectory(), "webapps/examples");
tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
tomcat.start();
tomcat.getConnector().stop();
- // This will throw an exception if the socket is not released
- ServerSocket socket = new ServerSocket(port);
- socket.close();
+ // This will should throw an Exception
+ Exception e = null;
+ ServerSocket s = null;
+ try {
+ s = new ServerSocket(port);
+ } catch (Exception e1) {
+ e = e1;
+ } finally {
+ if (s != null) {
+ try {
+ s.close();
+ } catch (Exception e2) { /* Ignore */ }
+ }
+ }
+ assertNotNull(e);
tomcat.getConnector().start();
}
-
- public void testDummy() throws Exception {
- // NO-OP
+
+ public void testStartStopBindOnStart() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ Connector c = tomcat.getConnector();
+ c.setProperty("bindOnInit", "false");
+
+ File appDir = new File(getBuildDirectory(), "webapps/examples");
+ tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
+
+ int port = getPort();
+
+ tomcat.start();
+
+ tomcat.getConnector().stop();
+ // This should not throw an Exception
+ Exception e = null;
+ ServerSocket s = null;
+ try {
+ s = new ServerSocket(port);
+ } catch (Exception e1) {
+ e = e1;
+ } finally {
+ if (s != null) {
+ try {
+ s.close();
+ } catch (Exception e2) { /* Ignore */ }
+ }
+ }
+ assertNull(e);
+ tomcat.getConnector().start();
}
}
<bug>50108</bug>: Add get/set methods for Connector property
minSpareThreads. Patch provided by Eiji Takahashi. (markt)
</add>
+ <fix>
+ <bug>50360</bug>: Provide an option to control when the socket
+ associated with a connector is bound. By default, the socket is bound on
+ <code>Connector.init()</code> and released on
+ <code>Connector.destroy()</code> as per the current behaviour but this
+ can be changed so that the socket is bound on
+ <code>Connector.start()</code> and released on
+ <code>Connector.stop()</code>. This fix also includes further Lifecycle
+ refactoring for Connectors and associated components. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Jasper">
associated with the server.</p>
</attribute>
+ <attribute name="bindOnInit" required="false">
+ <p>Controls when the socket used by the connector is bound. By default it
+ is bound when the connector is initiated and unbund when the connector is
+ destroyed. If set to <code>false</code>, the socket will be bound when the
+ connector is started and unbound when it is stopped.</p>
+ </attribute>
+
<attribute name="compressableMimeType" required="false">
<p>The value is a comma separated list of MIME types for which HTTP
compression may be used.