Get the server port number from tomcat without a request
Asked Answered
E

12

37

Is there any Tomcat API or configuration available which can tell an application (probably on startup), what port its running on without a request?

Imagine a scenario where there are two web applications running in the same Tomcat and one of which need to invoke a web service from the other one. We don't want the request to leave the Tomcat (if you use the Apache server name or absolute URL, the request will go out and come back again and it can go to any instance) and come back in. For that I know the name of the machine but no way to get the port number. I know I can hard code this information but I don't want to do this as I want my war file to be application server agnostic.

I know that we can find it if we have a HTTPServletRequest

This works only for Tomcat 6 and will not work on Tomcat 7

Endodontist answered 5/10, 2010 at 19:53 Comment(6)
I'm not sure why an application would need to know about the port. Can you provide some additional details on why you need this?Tinkling
do you want all available ports, or a specific port? An application server can listen on many ports (think http port 80 and https port 443 as an example).Britzka
@Sean I am not completely sure of it. Even if I we can get the details from a MBean, there should be a url:port for the MBean, right? Please elaborate and provide me an example if possible.Endodontist
@Teja I have updated my answer to address your concernMurguia
For interest, another valid use case for this is when making an outbound request to a remote system and needing to provide that system a callback address into the web app. In my case, there is no prior incoming request that allows me to dynamically identify the incoming port. None of the solution proposals below is sufficiently generic or server agnostic, so I think I will fall back to injecting the port from a properties file unfortunately.Tiertza
Do you know how to do this on tomcat 10?Bout
E
24

For anybody who is interested in how we solved this, here is the mock code

Server server = ServerFactory.getServer();
        Service[] services = server.findServices();
        for (Service service : services) {
            for (Connector connector : service.findConnectors()) {
                ProtocolHandler protocolHandler = connector.getProtocolHandler();
                if (protocolHandler instanceof Http11Protocol
                    || protocolHandler instanceof Http11AprProtocol
                    || protocolHandler instanceof Http11NioProtocol) {
                    serverPort = connector.getPort();
                    System.out.println("HTTP Port: " + connector.getPort());
                }
            }


        }
Endodontist answered 22/7, 2011 at 18:50 Comment(4)
Does not work on tomcat-7 - see #6834447Backrest
neither on jboss as 7 =\Burra
I had to use connector.getLocalPort() instead of connector.getPort(), the latter returned 0 when I had configured it to use a random port.Unblock
What package is ServerFactory in? (not seeing it in Tomcat 10)Bout
C
30

With this:

List<String> getEndPoints() throws MalformedObjectNameException,
        NullPointerException, UnknownHostException, AttributeNotFoundException,
        InstanceNotFoundException, MBeanException, ReflectionException {
    MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    QueryExp subQuery1 = Query.match(Query.attr("protocol"), Query.value("HTTP/1.1"));
    QueryExp subQuery2 = Query.anySubString(Query.attr("protocol"), Query.value("Http11"));
    QueryExp query = Query.or(subQuery1, subQuery2);
    Set<ObjectName> objs = mbs.queryNames(new ObjectName("*:type=Connector,*"), query);
    String hostname = InetAddress.getLocalHost().getHostName();
    InetAddress[] addresses = InetAddress.getAllByName(hostname);
    ArrayList<String> endPoints = new ArrayList<String>();
    for (Iterator<ObjectName> i = objs.iterator(); i.hasNext();) {
        ObjectName obj = i.next();
        String scheme = mbs.getAttribute(obj, "scheme").toString();
        String port = obj.getKeyProperty("port");
        for (InetAddress addr : addresses) {
            if (addr.isAnyLocalAddress() || addr.isLoopbackAddress() || 
                addr.isMulticastAddress()) {
                continue;
            }
            String host = addr.getHostAddress();
            String ep = scheme + "://" + host + ":" + port;
            endPoints.add(ep);
        }
    }
    return endPoints;
}

You will get a List like this:

[http://192.168.1.22:8080]
Coricoriaceous answered 31/7, 2013 at 13:43 Comment(0)
E
24

For anybody who is interested in how we solved this, here is the mock code

Server server = ServerFactory.getServer();
        Service[] services = server.findServices();
        for (Service service : services) {
            for (Connector connector : service.findConnectors()) {
                ProtocolHandler protocolHandler = connector.getProtocolHandler();
                if (protocolHandler instanceof Http11Protocol
                    || protocolHandler instanceof Http11AprProtocol
                    || protocolHandler instanceof Http11NioProtocol) {
                    serverPort = connector.getPort();
                    System.out.println("HTTP Port: " + connector.getPort());
                }
            }


        }
Endodontist answered 22/7, 2011 at 18:50 Comment(4)
Does not work on tomcat-7 - see #6834447Backrest
neither on jboss as 7 =\Burra
I had to use connector.getLocalPort() instead of connector.getPort(), the latter returned 0 when I had configured it to use a random port.Unblock
What package is ServerFactory in? (not seeing it in Tomcat 10)Bout
S
8
public void getIpAddressAndPort() 
throws MalformedObjectNameException, NullPointerException,
            UnknownHostException {

        MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();

        Set<ObjectName> objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"),
                Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));

        String host = InetAddress.getLocalHost().getHostAddress();
        String port = objectNames.iterator().next().getKeyProperty("port");

        System.out.println("IP Address of System : "+host );
        System.out.println("port of tomcat server : "+port);

    }
Signature answered 12/8, 2016 at 9:43 Comment(1)
While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. - From ReviewKowloon
W
2

The server port number doesn't exist. It can have any number of port numbers. So what you're asking doesn't make sense. The port number associated with a specific request does make sense.

Willy answered 18/11, 2010 at 1:8 Comment(0)
S
1

You could use crossContext. But I don't think that's app server agnostic.

I would share a custom class, behaving as a registry of running applications in the same tomcat instance through JNDI, as I explained here.

During startup, through a ContextListener or through an Spring container event, I would obtain the registry through a JNDI lookup, add my web app instance with an url obtained from the servletcontext.contextpath, and finally register a listener to hear other applications registering themselves. That's the more server agnostic I can think of.

Obtaining the port won't be server agnostic, you should use a context parameter.

EDIT: I'm sorry, forgot to say that what I've described is to share objects among contexts, but no, you can't not know the port unless you use some server API (not agnostic at all).

Saiz answered 7/10, 2010 at 23:9 Comment(0)
M
1
  • Get Hold of MBean/JMX Object for Tomcat/Server Instance
  • Get Virtual Server Instance Related Data from there

Check http://svn-mirror.glassfish.org/glassfish-svn/tags/embedded-gfv3-prelude-b07/web/web-glue/src/main/java/com/sun/enterprise/web/WebContainer.java for reference

The content of the MBeanServer can then be exposed through various protocols, implemented by protocol connectors[RMI/IIOP], or protocol adapters[SNMP/HTTP]. In this case, use of SNMP adapter will be a better approach so that a SNMP trap can be placed without knowing the exact IP/port of other Application Servers

Murguia answered 18/11, 2010 at 17:43 Comment(0)
A
1

These types of servers are designed to be able to listen on (almost) arbitrary ports and to hide these details from the contained applications which normally do not need to know.

The only way is to read the configuration files yourself and have access to the command line arguments that started the server where the configuration files may have been overridden. You have to know a lot about the system you are running on for this to work. There is no way of doing it portably.

Even if there were, there are cases in which it simply does not matter like being behind a NAT, certain firewalls, etc.

Auscultate answered 21/11, 2010 at 20:23 Comment(0)
G
1
public String getPort() {
    MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
    Set<ObjectName> objectNames;
    try {
        objectNames = beanServer.queryNames(new ObjectName("*:type=ProtocolHandler,*"),
                Query.match(Query.attr("name"), Query.value("\"http-*")));
    } catch (MalformedObjectNameException e) {
        LOGGER.error("Port not defined!", e);
    }

    return objectNames.iterator().next().getKeyProperty("port");
}

public String getSecurePort() {
    MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
    Set<ObjectName> objectNames;
    try {
        objectNames = beanServer.queryNames(new ObjectName("*:type=ProtocolHandler,*"),
                Query.match(Query.attr("name"), Query.value("\"https-*")));
    } catch (MalformedObjectNameException e) {
        LOGGER.error("SecuredPort not defined!", e);
    }

    return objectNames.iterator().next().getKeyProperty("port");
}
Gentleman answered 17/5, 2019 at 8:14 Comment(0)
D
0

I am not entirely sure if you can access the Tomcat port from code in the environment configuration you need. Did you consider actually having the full URL to the web service passed as a configuration param/setting (probably in a .properties file) to the app?

This way you wouldn't have to hardcode the port and de-couple both your apps so that you could technically have the web service on an external tomcat but still access it by just changing the property, avoiding code re-build.

Drollery answered 5/10, 2010 at 21:22 Comment(3)
Yes, we actually considered it, but the problem here is every machine has more than one tomcat instance (can this be any more complicated?) and obviously they run on different ports. So the port cannot be hardcoded.Endodontist
Are you saying that the web service (URL and port) moves around so often that passing it through a setting will not work? If so, it looks like there is a problem with the architecture. If not, then passing it through the settings wont really be hard coding it as long as we know (manually) where the web service (URL + port) resides on?Drollery
In a cluster there could be number of tomcat instances and my goal is to redirect the request from one app to another app should go to same tomcat instance not any random tomcat instance.Endodontist
N
0

Previously on a large distributed project, the design I used was to have the centralised service initialise the several services with the central service's URL(& port).

Obviously this means that the central service must maintain a list of the services (URL & port) to initialise.

Naked answered 16/11, 2010 at 11:46 Comment(0)
A
0

If you want to access an application on the same server instance, just omit the server part of the URL. Some examples what you can achieve. The current document is at http://example.com:8080/app2/doc.html

  • xxx.html becomes http://example.com:8080/app2/xxx.html
  • ../xxx.html becomes http://example.com:8080/xxx.html
  • ../xxx.html becomes http://example.com:8080/xxx.html
  • ../foo/xxx.html becomes http://example.com:8080/foo/xxx.html
  • ../../xxx.html becomes http://example.com:8080/xxx.html (there is no way to go beyond the root)
  • /xxx.html becomes http://example.com:8080/xxx.html This is probably what you look for.
  • //other.com/xxx.html becomes http://example.com:8080/xxx.html Useful if you want to keep "https:"
Antoinetteanton answered 19/11, 2010 at 14:16 Comment(4)
But the question is about detecting the listener port.Murguia
No, the question is actually how to access another application on the same server.Antoinetteanton
-1 Teja said "I know that we can find it if we have a HTTPServletRequest", and your answer implies an incoming request.Blueprint
@mrrtnn: sigh He doesn't need one. He doesn't need the port number. All he needs is the relative URL of the service he wants to access at the time he needs to access it.Antoinetteanton
C
-1

Hmm, how would an application get started in Tomcat without a request? Maybe I'm going brain dead for a moment here, but I don't think any classes will load until a request hits. Sure, you could have classes independent of any particular request, but they'd need a request to get them fired off at some point.

Cleanse answered 5/10, 2010 at 20:42 Comment(1)
I mean when the application deploys in any application server, there will be some bootstrap code or init params in servlets which bring application up and running ready to serve requests. When spring MVC application loads it loads all the configuration and I need to know the port number in this process. I will update the question with more details.Endodontist

© 2022 - 2024 — McMap. All rights reserved.