/* * Copyright 2013 Atteo. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.atteo.moonshine.tomcat; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.LifecycleException; import static org.apache.catalina.LifecycleState.STARTED; import org.apache.catalina.connector.Connector; import org.apache.catalina.core.StandardHost; import org.apache.catalina.startup.Tomcat; import org.atteo.config.XmlDefaultValue; import org.atteo.moonshine.ServiceConfiguration; import org.atteo.moonshine.services.Service; import org.atteo.moonshine.webserver.WebServerAddress; import org.atteo.moonshine.webserver.WebServerService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.AbstractModule; import com.google.inject.Module; import com.google.inject.servlet.GuiceFilter; /** * Starts Tomcat. * * <p> * Tomcat support is currently very basic. You should choose Jetty most of the time. * </p> */ @XmlRootElement(name = "tomcat") @ServiceConfiguration(autoConfiguration = "" + "<connectors>" + " <connector>" + " <port>${oneof:${tomcat.port},${webserver.port},}</port>" + " </connector>" + "</connectors>") public class TomcatService extends WebServerService { /** * Tomcat base directory. */ @XmlElement @XmlDefaultValue("${dataHome}/tomcat") private String baseDir; @XmlElementWrapper(name = "connectors") @XmlElement(name = "connector") private List<TomcatConnectorConfig> connectors = new ArrayList<TomcatConnectorConfig>() { private static final long serialVersionUID = 1L; { add(new TomcatConnectorConfig()); } }; /** * Tomcat default host. * <p> * If not provided will be automatically set to the name of the first host in {@link #hosts}. * </p> */ @XmlElement private String defaultHost = null; @XmlElementWrapper(name = "hosts") @XmlElement(name = "host") private List<HostConfig> hosts = new ArrayList<HostConfig>() { { add(new HostConfig()); } }; private Tomcat tomcat = null; @Override public Module configure() { return new AbstractModule() { @Override protected void configure() { bind(GuiceFilter.class); try { Files.createDirectories(Paths.get(baseDir)); } catch (IOException e) { throw new RuntimeException(e); } bind(WebServerAddress.class).toInstance(new WebServerAddress() { @Override public int getPort() { return tomcat.getConnector().getLocalPort(); } @Override public String getHost() { return tomcat.getHost().getName(); } @Override public String getUrl() { String host = getHost(); if (host == null) { host = "localhost"; } return tomcat.getConnector().getScheme() + "://" + host + ":" + getPort(); } }); } }; } private void init() { tomcat = new Tomcat(); tomcat.setBaseDir(baseDir); if (defaultHost != null) { tomcat.getEngine().setDefaultHost(defaultHost); } for (HostConfig hostConfig : hosts) { StandardHost host = new StandardHost(); host.setAppBase(hostConfig.getAppBase()); host.setName(hostConfig.getName()); if (defaultHost == null) { defaultHost = hostConfig.getName(); tomcat.getEngine().setDefaultHost(defaultHost); } for (ContextConfig contextConfig : hostConfig.getContexts()) { Context context = tomcat.addContext(host, contextConfig.getPath(), contextConfig.getBaseDir()); contextConfig.configure(context); } tomcat.getEngine().addChild(host); tomcat.setHost(host); } for (TomcatConnectorConfig connectorConfig : connectors) { Connector connector = new Connector(connectorConfig.getProtocol()); connector.setPort(connectorConfig.getPort()); tomcat.setConnector(connector); tomcat.getService().addConnector(connector); } } @Override public void start() { if (tomcat == null) { init(); } try { tomcat.start(); if (tomcat.getConnector().getState() != STARTED) { throw new RuntimeException("Cannot start Tomcat, check logs"); } for (Container container : tomcat.getHost().findChildren()) { if (container.getState() != STARTED) { throw new RuntimeException("Cannot start Tomcat, check logs"); } } printPorts(); } catch (LifecycleException e) { throw new RuntimeException(e); } } @Override public void stop() { try { tomcat.stop(); } catch (LifecycleException e) { throw new RuntimeException(e); } } @Override public Iterable<? extends Service> getSubServices() { List<Service> result = new ArrayList<>(); for (TomcatConnectorConfig connector : connectors) { if (connector instanceof Service) { result.add((Service) connector); } } for (HostConfig host : hosts) { for (ContextConfig context : host.getContexts()) { if (context instanceof Service) { result.add(context); } } } return result; } private void printPorts() { Logger logger = LoggerFactory.getLogger("Moonshine"); logger.info(" Tomcat started on {}:{}",tomcat.getHost().getName(), tomcat.getConnector().getLocalPort()); } }