Bug 61171

Summary: Add port offset attribute (portOffset?) to Server configuration in server.xml
Product: Tomcat 9 Reporter: Coty Sutherland <csutherl>
Component: CatalinaAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED FIXED    
Severity: enhancement    
Priority: P2    
Version: unspecified   
Target Milestone: -----   
Hardware: PC   
OS: Linux   

Description Coty Sutherland 2017-06-09 17:42:11 UTC
An option that would allow users to set a port offset in the configuration (or with a system property) would be great for creating new instances on the same machine. Such an option would prevent the need for manually updating the connectors, etc in the server.xml for each new installation on the same host. With this option in place, users could simply unzip multiple installs and then start each with a different offset out of the box rather than updating multiple places in the server.xml.

As an example of the implementation, setting the option to 100 would
add 100 to all port bindings on the server (e.g. with a vanilla
install it would cause tomcat to run on 8105, 8180, and 8109 rather than 8005, 8180, and 8009). It should also be noted that this same offset should apply to ALL port bindings and references, include the redirectPort.
Comment 1 Igal Sapir 2017-06-10 06:17:14 UTC
Please see https://github.com/apache/tomcat/pull/63
Comment 2 Igal Sapir 2017-06-10 17:25:04 UTC
Comments from Mark Thomas on Github (https://github.com/apache/tomcat/pull/63#issuecomment-307556995):

I think more discussion is required on how to approach this. Specifically, when should the offset be applied? What is the impact on getters and setters? For example connector.setPort (getPort ()) should be a NO-OP. What is reported via JMX? There are all sorts of side effects that need to be thought through here else we'll end up creating some very non-intuative behaviour.
Comment 3 Igal Sapir 2017-06-10 17:27:51 UTC
(moving discussion from Github to the ticket):

>I think more discussion is required on how to approach this. Specifically, when should the offset be applied?

I agree, and I was trying to find different options for this in order to initiate a discussion, but the way that Tomcat reads the config files and initializes objects via introspection really limits us here unless there is a major overhaul.

Server has the value for portOffset, so a reference to it is required in order to calculate (port + portOffset)

The process is somewhat like this:

Connectors are initialized (no reference to Service or Server)
Service is linked to the Connectors (still no reference to Server)
Server is added to Service
Point (3) is point where I took the portOffset from Server and applied it to the Connectors.

>What is the impact on getters and setters? For example connector.setPort (getPort ()) should be a NO-OP.

At point (1) above the Connector's setPort() is called by the IntrospectionUtils with the values from server.xml. That can not be a NO-OP at that point as that would practically disable the Connector, but we can check if Service and Server are null or not (which they are at initialization) and perhaps do something different accordingly?

Is it even possible/allowed to change the port after initialization?
Comment 4 Igal Sapir 2017-06-21 19:46:46 UTC
Can we discuss the guidelines/specification for this feature?  Anyone wants to  contribute their thoughts?
Comment 5 Pradip Bhattacharya 2017-07-02 13:07:50 UTC
port offset is a good thing to have, can help a lot in having multiple installations/resolving port conflicts.

I have tried to implement it by adding portOffset attribute in Server element.

Below is the change in server.xml, portOffset as the new attribute:

<Server port="8005" shutdown="SHUTDOWN" portOffset="1000">
...
</Server>

Added getter/setter method portOffset in org.apache.catalina.Server interface. So StandardServer now has a member variable portOffset, and its respective getter/setter functions.

Now in order to update all the port/redirectPort numbers mentioned in the server.xml, I have created a Rule, i.e. AddPortOffsetRule, which is applied after values of all the attributes have been set in the target(current) object.

For example, in Catalina::createStartDigester function,
after digester.addSetProperties("Server"); is called,
a new is being invoked by adding the below line
digester.addRule("Server", new AddPortOffsetRule("port"));

Similarly for Connector, below 2 rules are added to update port numbers:
digester.addRule("Server/Service/Connector",new AddPortOffsetRule("port"));
digester.addRule("Server/Service/Connector",new AddPortOffsetRule("redirectPort"));

This way the port related member variables are again updated using the port offset.
Comment 6 Mark Thomas 2017-07-05 09:33:45 UTC
My suggestion is as follows:

Add a new getter for every getPort() or equivalent named getPortWithOffset() and use it in place of getPort() whenever code needs to get the actual port number to open. That would ensure that getPort()/setPort() (and getOffset()/setOffset() ) remain symmetrical so they continue to work with features like configuration saving.

portWithOffset should be exposed as a read-only JMX attribute everywhere port is exposed.

It does mean users using an offset will need to look at a different attribute to find the port they are actually using. That might confuse some tools but on balance I think this is lower risk than getPort()/setPort() not being symmetrical.
Comment 7 Mark Thomas 2018-10-10 14:44:34 UTC
See https://github.com/apache/tomcat/pull/125
Comment 8 Mark Thomas 2018-11-01 15:15:21 UTC
Provide patch applied (with a few changes) to 9.0.x for 9.0.13 onwards.