--- a/java/org/apache/catalina/connector/Connector.java +++ a/java/org/apache/catalina/connector/Connector.java @@ -114,7 +114,7 @@ public class Connector extends LifecycleMBeanBase { /** * The port number on which we listen for requests. */ - protected int port = 0; + protected int port = -1; /** @@ -497,7 +497,9 @@ public class Connector extends LifecycleMBeanBase { } /** - * Return the port number on which we listen for requests. + * Return the port number on which this connector is configured to listen + * for requests. The special value of 0 means select a random free port + * when the socket is bound. */ public int getPort() { @@ -520,6 +522,16 @@ public class Connector extends LifecycleMBeanBase { /** + * Return the port number on which this connector is listening to requests. + * If the special value for {@link #port} of zero is used then this method + * will report the actual port bound. + */ + public int getLocalPort() { + return ((Integer) getProperty("localPort")).intValue(); + } + + + /** * Return the Coyote protocol handler in use. */ public String getProtocol() { @@ -938,7 +950,7 @@ public class Connector extends LifecycleMBeanBase { protected void startInternal() throws LifecycleException { // Validate settings before starting - if (getPort() < 1) { + if (getPort() < 0) { throw new LifecycleException(sm.getString( "coyoteConnector.invalidPort", Integer.valueOf(getPort()))); } --- a/java/org/apache/catalina/connector/mbeans-descriptors.xml +++ a/java/org/apache/catalina/connector/mbeans-descriptors.xml @@ -108,7 +108,11 @@ type="int"/> + + --- a/java/org/apache/coyote/AbstractProtocol.java +++ a/java/org/apache/coyote/AbstractProtocol.java @@ -194,6 +194,8 @@ public abstract class AbstractProtocol implements ProtocolHandler, } + public int getLocalPort() { return endpoint.getLocalPort(); } + /* * When Tomcat expects data from the client, this is the time Tomcat will * wait for that data to arrive before closing the connection. --- a/java/org/apache/tomcat/util/net/AbstractEndpoint.java +++ a/java/org/apache/tomcat/util/net/AbstractEndpoint.java @@ -141,6 +141,7 @@ public abstract class AbstractEndpoint { public int getPort() { return port; } public void setPort(int port ) { this.port=port; } + public abstract int getLocalPort(); /** * Address for the server socket. --- a/java/org/apache/tomcat/util/net/AprEndpoint.java +++ a/java/org/apache/tomcat/util/net/AprEndpoint.java @@ -37,6 +37,7 @@ import org.apache.tomcat.jni.Pool; import org.apache.tomcat.jni.SSL; import org.apache.tomcat.jni.SSLContext; import org.apache.tomcat.jni.SSLSocket; +import org.apache.tomcat.jni.Sockaddr; import org.apache.tomcat.jni.Socket; import org.apache.tomcat.jni.Status; import org.apache.tomcat.util.ExceptionUtils; @@ -326,8 +327,29 @@ public class AprEndpoint extends AbstractEndpoint { public void setSSLInsecureRenegotiation(boolean SSLInsecureRenegotiation) { this.SSLInsecureRenegotiation = SSLInsecureRenegotiation; } public boolean getSSLInsecureRenegotiation() { return SSLInsecureRenegotiation; } - // --------------------------------------------------------- Public Methods + /** + * Port in use. + */ + @Override + public int getLocalPort() { + long s = serverSock; + if (s == 0) { + return -1; + } else { + long sa; + try { + sa = Address.get(Socket.APR_LOCAL, s); + Sockaddr addr = Address.getInfo(sa); + return addr.port; + } catch (Exception e) { + return -1; + } + } + } + + + // --------------------------------------------------------- Public Methods /** * Number of keepalive sockets. --- a/java/org/apache/tomcat/util/net/JIoEndpoint.java +++ a/java/org/apache/tomcat/util/net/JIoEndpoint.java @@ -96,6 +96,18 @@ public class JIoEndpoint extends AbstractEndpoint { public void setServerSocketFactory(ServerSocketFactory factory) { this.serverSocketFactory = factory; } public ServerSocketFactory getServerSocketFactory() { return serverSocketFactory; } + /** + * Port in use. + */ + @Override + public int getLocalPort() { + ServerSocket s = serverSocket; + if (s == null) { + return -1; + } else { + return s.getLocalPort(); + } + } /* * Optional feature support. --- a/java/org/apache/tomcat/util/net/NioEndpoint.java +++ a/java/org/apache/tomcat/util/net/NioEndpoint.java @@ -21,6 +21,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; +import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.nio.ByteBuffer; @@ -402,6 +403,26 @@ public class NioEndpoint extends AbstractEndpoint { public SSLContext getSSLContext() { return sslContext;} public void setSSLContext(SSLContext c) { sslContext = c;} + + /** + * Port in use. + */ + @Override + public int getLocalPort() { + ServerSocketChannel ssc = serverSock; + if (ssc == null) { + return -1; + } else { + ServerSocket s = ssc.socket(); + if (s == null) { + return -1; + } else { + return s.getLocalPort(); + } + } + } + + // --------------------------------------------------------- OOM Parachute Methods protected void checkParachute() { --- a/test/org/apache/catalina/connector/TestConnector.java +++ a/test/org/apache/catalina/connector/TestConnector.java @@ -19,6 +19,7 @@ package org.apache.catalina.connector; import java.net.SocketTimeoutException; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import org.junit.Test; @@ -69,4 +70,26 @@ public class TestConnector extends TomcatBaseTest { } assertEquals(503, rc); } + + + @Test + public void testPort() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + Connector connector1 = tomcat.getConnector(); + connector1.setPort(0); + + Connector connector2 = new Connector(); + connector2.setPort(0); + + tomcat.getService().addConnector(connector2); + + tomcat.start(); + + int localPort1 = connector1.getLocalPort(); + int localPort2 = connector2.getLocalPort(); + + assertTrue(localPort1 > 0); + assertTrue(localPort2 > 0); + } }