Получить полный URL-адрес сервлета во время запуска под Tomcat 7

У меня есть веб-приложение, написанное с использованием Tomcat 6, и я пытаюсь заставить его работать с Tomcat 7. Во время запуска приложение, помимо прочего, регистрирует свой компонент веб-службы в каком-то удаленном каталоге. Для этого ему необходимо указать собственный URL. Следующий (немного наивный) метод должен возвращать URL-адрес веб-сервиса:

import org.apache.catalina.ServerFactory;
import org.apache.catalina.connector.Connector;
.
.
.
private String getWsUrl(ServletContext context)
            throws UnknownHostException, MalformedURLException {
    String host = java.net.InetAddress.getLocalHost().getCanonicalHostName();
    int port = -1;
    for (Connector c : ServerFactory.getServer().findServices()[0].findConnectors()) {
        if (c.getProtocol().contains("HTTP")) {
            port = c.getPort();
            break;
        }
    }
    URL wsURL = new URL("http", host, port, context.getContextPath()
                + C.WEB_SERVICE_PATH /* this is just a constant string */ );
    return wsURL.toString();
}

Часть ServerFactory.getServer() оказалась проблематичной: в Tomcat 7 нет класса org.apache.catalina.ServerFactory. Любые предложения о том, как переписать это для Tomcat 7? Я также был бы рад иметь более переносимый, не специфичный для tomcat код.


person Ilia K.    schedule 18.09.2010    source источник


Ответы (2)


Я столкнулся с той же проблемой: мне нужно было знать номер порта для создания URL-адреса для конкретного экземпляра Tomcat, номер порта может отличаться (поскольку я запускаю несколько экземпляров для тестирования), а начиная с Tomcat 7 ServerFactory ушел.

Я написал следующий код, чтобы найти и проанализировать файл Tomcat server.xml. Он не анализирует много, просто получает значения HTTP «port» и «redirectPort». Это зависит от системных свойств «catalina.home» или «catalina.base», которые должны существовать в любом запущенном экземпляре Tomcat. Прелесть в том, что он не зависит ни от каких классов Tomcat и использует синтаксический анализатор XML JVM.

Надеюсь, это поможет.

public final class TomcatConfigUtil {

private static final String CONFIG_FILE_PATH = "conf/server.xml";

private static Map<String, String> properties = null;

// No instances, please.
private TomcatConfigUtil() { }


/**
 * Get the configuration as a map of name/value pairs, or throw an exception if it wasn't found.
 * All values are returned as Strings.
 * <ul>
 *   <li> httpPort - the HTTP port</li>
 *   <li> httpRedirectPort - the HTTP redirect port (which seems to be the SSL port) </li>
 * </ul>
 * @exception FileNotFoundException if the configuration file wasn't found
 * @exception IOException if there was a problem reading the configuration file
 * @exception SAXException if there was a problem parsing the configuration file
 */
public static synchronized Map<String, String> getConfig() throws FileNotFoundException, IOException, SAXException {

    if (properties != null) {

        return properties;
    }

    final File serverConfigFile = findServerConfigFile();
    if (serverConfigFile == null) {

        throw new FileNotFoundException("Couldn't find the configuration file.");
    }

    final Map<String, String> tmpProperties = new HashMap<String, String>();

    // Content-handler does the actual parsing.
    final ServerConfigContentHandler contentHandler = new ServerConfigContentHandler(tmpProperties);
    final XMLReader xmlReader = XMLReaderFactory.createXMLReader();
    xmlReader.setContentHandler(contentHandler);

    // Pass the config file as the input source for parsing.
    final FileReader fileReader = new FileReader(serverConfigFile);
    xmlReader.parse(new InputSource(fileReader));
    fileReader.close();

    return (properties = Collections.unmodifiableMap(tmpProperties));
}


private static File findServerConfigFile() {

    if (System.getProperty("catalina.home") != null) {

        final File file = new File(System.getProperty("catalina.home"), CONFIG_FILE_PATH);
        if (file.isFile()) {

            return file;
        }
    }

    if (System.getProperty("catalina.base") != null) {

        final File file = new File(System.getProperty("catalina.base"), CONFIG_FILE_PATH);
        if (file.isFile()) {

            return file;
        }
    }

    return null;
}


/**
 * ContentHandler implementation for the XML parser.
 */
private static class ServerConfigContentHandler implements ContentHandler {

    private final Map<String, String> map;
    private boolean inServerElement;
    private boolean inCatalinaServiceElement;


    private ServerConfigContentHandler(final Map<String, String> map) {

        this.map = map;
    }

    @Override
    public void startDocument() throws SAXException {

        this.inServerElement = false;
        this.inCatalinaServiceElement = false;
    }

    @Override
    public void startElement(final String uri, final String localName, final String qName, final Attributes atts) throws SAXException {

        if (!this.inServerElement && "Server".equals(localName)) {

            this.inServerElement = true;
        }
        else if (this.inServerElement && "Service".equals(localName) && "Catalina".equals(atts.getValue("name"))) {

            this.inCatalinaServiceElement = true;
        }
        else if (this.inCatalinaServiceElement && "Connector".equals(localName) && "HTTP/1.1".equals(atts.getValue("protocol"))) {

            if ((atts.getValue("SSLEnabled") == null || "false".equals(atts.getValue("SSLEnabled"))) &&
                    (atts.getValue("secure") == null || "false".equals(atts.getValue("secure"))) &&
                    (atts.getValue("scheme") == null || "http".equals(atts.getValue("scheme")))) {

                        final String portStr = atts.getValue("port");
                        if (portStr != null) {

                            this.map.put("httpPort", portStr);
                        }
                        final String redirectPortStr = atts.getValue("redirectPort");
                        if (redirectPortStr != null) {

                            this.map.put("httpRedirectPort", redirectPortStr);
                        }
                    }
        }
    }

    @Override
    public void endElement(final String uri, final String localName, final String qName) throws SAXException {

        if (this.inCatalinaServiceElement && "Service".equals(localName)) {

            this.inCatalinaServiceElement = false;
        }
        else if (this.inServerElement && "Server".equals(localName)) {

            this.inServerElement = false;
        }
    }

    @Override
    public void endDocument() throws SAXException {

        this.inServerElement = false;
        this.inCatalinaServiceElement = false;
    }

    @Override
    public void characters(final char[] ch, final int start, final int length) throws SAXException { }

    @Override
    public void endPrefixMapping(final String prefix) throws SAXException { }

    @Override
    public void ignorableWhitespace(final char[] ch, final int start, final int length) throws SAXException { }

    @Override
    public void processingInstruction(final String target, final String data) throws SAXException { }

    @Override
    public void setDocumentLocator(final Locator locator) { }

    @Override
    public void skippedEntity(final String name) throws SAXException { }

    @Override
    public void startPrefixMapping(final String prefix, final String uri) throws SAXException { }
}

}

person RichW    schedule 08.09.2011
comment
Интересный подход, хотя и слишком грязный для производственного кода. +1 за оригинальность. - person Ilia K.; 10.09.2011
comment
Это работает, однако связывает использование с Tomcat 7. Кроме того, не для этого ответа, а для исходного сообщения, производительность .getLocalhost() в некоторых системах очень низкая, вероятно, из-за поиска IP6 из Java 1.4.1. Интересно, можем ли мы сделать запрос дампа на сам сервлет после запуска, чтобы получить необходимую информацию через HttpRequest. - person Casper Ngo; 07.03.2014

Я никогда не использовал его, и он, вероятно, не возвращает URL-адрес с именем хоста и адресом, но есть ли шанс, что ServletContext.getResource("/") сделает то, что вы хотите? Я знаю, что он предназначен для доступа к ресурсу внутри сервлета, но вы никогда не знаете наверняка.

person Tim Yates    schedule 18.09.2010
comment
Он возвращает файловую систему диска на основе URL, указывающую на корень общедоступного веб-контента. Так что нет, это абсолютно не делает то, что хочет ОП :) - person BalusC; 20.09.2010
comment
Хорошо. В документах API не указано, какой URL он возвращает, и у меня не было времени его протестировать. Спасибо, что проверили это. - person Tim Yates; 20.09.2010