001/* 002 * (C) Copyright 2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019package org.nuxeo.runtime.server.tomcat; 020 021import java.io.File; 022import java.util.Map; 023import java.util.Map.Entry; 024 025import javax.servlet.ServletException; 026 027import org.apache.catalina.Container; 028import org.apache.catalina.Context; 029import org.apache.catalina.LifecycleException; 030import org.apache.catalina.Wrapper; 031import org.apache.catalina.connector.Connector; 032import org.apache.catalina.startup.Tomcat; 033import org.apache.tomcat.util.descriptor.web.FilterDef; 034import org.apache.tomcat.util.descriptor.web.FilterMap; 035import org.apache.tomcat.util.scan.StandardJarScanner; 036import org.nuxeo.common.Environment; 037import org.nuxeo.common.server.WebApplication; 038import org.nuxeo.runtime.RuntimeServiceException; 039import org.nuxeo.runtime.server.FilterDescriptor; 040import org.nuxeo.runtime.server.FilterMappingDescriptor; 041import org.nuxeo.runtime.server.ServerConfigurator; 042import org.nuxeo.runtime.server.ServletContextListenerDescriptor; 043import org.nuxeo.runtime.server.ServletDescriptor; 044 045/** 046 * Configurator for an embedded Tomcat server. 047 * 048 * @since 10.2 049 */ 050public class TomcatServerConfigurator implements ServerConfigurator { 051 052 public static final int DEFAULT_PORT = 8080; 053 054 protected Tomcat tomcat; 055 056 @Override 057 public int initialize(int port) { 058 if (port <= 0) { 059 port = DEFAULT_PORT; 060 } 061 tomcat = new Tomcat(); 062 tomcat.setBaseDir("."); // for tmp dir 063 tomcat.setHostname("localhost"); 064 tomcat.setPort(port); 065 Connector connector = tomcat.getConnector(); 066 connector.setProperty("maxKeepAliveRequests", "1"); // vital for clean shutdown 067 connector.setProperty("socket.soReuseAddress", "true"); 068 return port; 069 } 070 071 @Override 072 public void close() { 073 tomcat = null; 074 } 075 076 @Override 077 public void start() { 078 try { 079 tomcat.start(); 080 } catch (LifecycleException e) { 081 throw new RuntimeServiceException(e); 082 } 083 } 084 085 @Override 086 public void stop() { 087 if (tomcat == null) { 088 return; 089 } 090 try { 091 tomcat.stop(); 092 tomcat.destroy(); 093 } catch (LifecycleException e) { 094 throw new RuntimeServiceException(e); 095 } 096 } 097 098 @Override 099 public void addWepApp(WebApplication descriptor) { 100 String contextPath = normalizeContextPath(descriptor.getContextPath()); 101 102 File home = Environment.getDefault().getHome(); 103 File docBase = new File(home, descriptor.getWebRoot()); 104 docBase.mkdirs(); // make sure the WAR root exists 105 try { 106 Context context = tomcat.addWebapp(contextPath, docBase.getAbsolutePath()); 107 StandardJarScanner jarScanner = (StandardJarScanner) context.getJarScanner(); 108 // avoid costly scanning, we register everything explicitly 109 jarScanner.setScanManifest(false); // many MANIFEST.MF files have incorrect Class-Path 110 jarScanner.setScanAllDirectories(false); 111 jarScanner.setScanAllFiles(false); 112 jarScanner.setScanBootstrapClassPath(false); 113 jarScanner.setScanClassPath(false); 114 } catch (ServletException e) { 115 throw new RuntimeServiceException(e); 116 } 117 } 118 119 @Override 120 public void addFilter(FilterDescriptor descriptor) { 121 String name = descriptor.getName(); 122 Context context = getContextForPath(descriptor.getContext()); 123 FilterDef filterDef = new FilterDef(); 124 filterDef.setFilterName(name); 125 filterDef.setDisplayName(descriptor.getDisplayName()); 126 filterDef.setFilterClass(descriptor.getClazz().getName()); 127 Map<String, String> initParams = descriptor.getInitParams(); 128 if (initParams != null) { 129 filterDef.getParameterMap().putAll(initParams); 130 } 131 context.addFilterDef(filterDef); 132 for (FilterMappingDescriptor fmd : descriptor.getFilterMappings()) { 133 FilterMap filterMap = new FilterMap(); 134 filterMap.setFilterName(name); 135 filterMap.addServletName("*"); 136 filterMap.addURLPatternDecoded(fmd.getUrlPattern()); 137 for (String dispatch : fmd.getDispatchers()) { 138 filterMap.setDispatcher(dispatch); 139 } 140 context.addFilterMap(filterMap); 141 } 142 } 143 144 @Override 145 public void addServlet(ServletDescriptor descriptor) { 146 String name = descriptor.getName(); 147 Context context = getContextForPath(descriptor.getContext()); 148 // remove existing servlet, to allow overrides (usually to change init params) 149 Container previous = context.findChild(name); 150 if (previous != null) { 151 context.removeChild(previous); 152 } 153 Wrapper servlet = Tomcat.addServlet(context, name, descriptor.getClazz().getName()); 154 Map<String, String> initParams = descriptor.getInitParams(); 155 if (initParams != null) { 156 for (Entry<String, String> es : initParams.entrySet()) { 157 servlet.addInitParameter(es.getKey(), es.getValue()); 158 } 159 } 160 for (String urlPattern : descriptor.getUrlPatterns()) { 161 context.addServletMappingDecoded(urlPattern, name); 162 } 163 } 164 165 @Override 166 public void addLifecycleListener(ServletContextListenerDescriptor descriptor) { 167 Context context = getContextForPath(descriptor.getContext()); 168 context.addApplicationListener(descriptor.getClazz().getName()); 169 } 170 171 protected Context getContextForPath(String contextPath) { 172 contextPath = normalizeContextPath(contextPath); 173 Context context = (Context) tomcat.getHost().findChild(contextPath); 174 if (context == null) { 175 context = tomcat.addContext(contextPath, null); 176 } 177 return context; 178 } 179 180 protected String normalizeContextPath(String contextPath) { 181 if (contextPath.equals("/")) { 182 contextPath = ""; 183 } 184 return contextPath; 185 } 186 187}