Author: taher
Date: Fri Jun 23 21:34:48 2017 New Revision: 1799708 URL: http://svn.apache.org/viewvc?rev=1799708&view=rev Log: Implemented: Refactor CatalinaContainer (OFBIZ-9392) No functional change, but an almost complete rewrite of the tomcat container with the following highlights: - breakup the logic in init() many smaller functions each specializing in one thing - Unify the host creation logic between main host and context-specific hosts - introduce streams and lambdas where appropriate - rename loadComponents() to loadWebapps() - rename createContext() to createCallableContext() - rename configureContext() to prepareContext() - remove instance variables that are not necessary / redundant for operating the container correctly and refactor the code logic accordingly - remove unnecessary valve comments and point to documentation URL - remove any commented out code - remove the static block for initializing SSLUtil.loadJsseProperties(). This - code is already called and hence redundant - remove redundant / dead / unused code - add missing FilterDef to context - rename J2EE server from "ofbiz container 3.1" to "ofbiz container" - lots and lots of re-arranging and small code improvements Thanks: Jacopo Capellato, Michael Brohl, Scott Gray and Jacques Le Roux for your help in testing, reviewing and providing feedback. Modified: ofbiz/ofbiz-framework/trunk/framework/catalina/src/main/java/org/apache/ofbiz/catalina/container/CatalinaContainer.java Modified: ofbiz/ofbiz-framework/trunk/framework/catalina/src/main/java/org/apache/ofbiz/catalina/container/CatalinaContainer.java URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/catalina/src/main/java/org/apache/ofbiz/catalina/container/CatalinaContainer.java?rev=1799708&r1=1799707&r2=1799708&view=diff ============================================================================== --- ofbiz/ofbiz-framework/trunk/framework/catalina/src/main/java/org/apache/ofbiz/catalina/container/CatalinaContainer.java (original) +++ ofbiz/ofbiz-framework/trunk/framework/catalina/src/main/java/org/apache/ofbiz/catalina/container/CatalinaContainer.java Fri Jun 23 21:34:48 2017 @@ -19,33 +19,36 @@ package org.apache.ofbiz.catalina.container; import java.io.File; -import java.net.MalformedURLException; +import java.io.IOException; import java.net.URL; import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; +import java.util.stream.Collectors; import javax.naming.InitialContext; import javax.naming.NamingException; +import javax.xml.parsers.ParserConfigurationException; -import org.apache.catalina.Cluster; import org.apache.catalina.Context; import org.apache.catalina.Engine; import org.apache.catalina.Globals; import org.apache.catalina.Host; import org.apache.catalina.LifecycleException; -import org.apache.catalina.Manager; +import org.apache.catalina.Valve; import org.apache.catalina.connector.Connector; import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardEngine; import org.apache.catalina.core.StandardHost; import org.apache.catalina.core.StandardServer; import org.apache.catalina.filters.RequestDumperFilter; +import org.apache.catalina.ha.ClusterManager; import org.apache.catalina.ha.tcp.ReplicationValve; import org.apache.catalina.ha.tcp.SimpleTcpCluster; import org.apache.catalina.loader.WebappLoader; @@ -68,135 +71,81 @@ import org.apache.ofbiz.base.component.C import org.apache.ofbiz.base.concurrent.ExecutionPool; import org.apache.ofbiz.base.container.Container; import org.apache.ofbiz.base.container.ContainerConfig; +import org.apache.ofbiz.base.container.ContainerConfig.Configuration; import org.apache.ofbiz.base.container.ContainerConfig.Configuration.Property; import org.apache.ofbiz.base.container.ContainerException; import org.apache.ofbiz.base.location.FlexibleLocation; import org.apache.ofbiz.base.start.Start; import org.apache.ofbiz.base.start.StartupCommand; import org.apache.ofbiz.base.util.Debug; -import org.apache.ofbiz.base.util.SSLUtil; import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.base.util.UtilXml; import org.w3c.dom.Document; - -/* - * --- Access Log Pattern Information - From Tomcat 5 AccessLogValve.java - * <p>Patterns for the logged message may include constant text or any of the - * following replacement strings, for which the corresponding information - * from the specified Response is substituted:</p> - * <ul> - * <li><b>%a</b> - Remote IP address - * <li><b>%A</b> - Local IP address - * <li><b>%b</b> - Bytes sent, excluding HTTP headers, or '-' if no bytes - * were sent - * <li><b>%B</b> - Bytes sent, excluding HTTP headers - * <li><b>%h</b> - Remote host name - * <li><b>%H</b> - Request protocol - * <li><b>%l</b> - Remote logical username from identd (always returns '-') - * <li><b>%m</b> - Request method - * <li><b>%p</b> - Local port - * <li><b>%q</b> - Query string (prepended with a '?' if it exists, otherwise - * an empty string - * <li><b>%r</b> - First line of the request - * <li><b>%s</b> - HTTP status code of the response - * <li><b>%S</b> - User session ID - * <li><b>%t</b> - Date and time, in Common Log Format format - * <li><b>%u</b> - Remote user that was authenticated - * <li><b>%U</b> - Requested URL path - * <li><b>%v</b> - Local server name - * <li><b>%D</b> - Time taken to process the request, in millis - * <li><b>%T</b> - Time taken to process the request, in seconds - * </ul> - * <p>In addition, the caller can specify one of the following aliases for - * commonly utilized patterns:</p> - * <ul> - * <li><b>common</b> - <code>%h %l %u %t "%r" %s %b</code> - * <li><b>combined</b> - - * <code>%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"</code> - * </ul> - * - * <p> - * There is also support to write information from the cookie, incoming - * header, the Session or something else in the ServletRequest.<br/> - * It is modeled after the apache syntax: - * <ul> - * <li><code>%{xxx}i</code> for incoming headers - * <li><code>%{xxx}c</code> for a specific cookie - * <li><code>%{xxx}r</code> xxx is an attribute in the ServletRequest - * <li><code>%{xxx}s</code> xxx is an attribute in the HttpSession - * </ul> - * </p> - */ +import org.xml.sax.SAXException; /** * CatalinaContainer - Tomcat * + * For more information about the AccessLogValve pattern visit the + * <a href="https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Access_Log_Valve">Documentation</a> */ public class CatalinaContainer implements Container { - public static final String CATALINA_HOSTS_HOME = System.getProperty("ofbiz.home") + "/framework/catalina/hosts"; - public static final String J2EE_SERVER = "OFBiz Container 3.1"; - public static final String J2EE_APP = "OFBiz"; public static final String module = CatalinaContainer.class.getName(); - private static final ThreadGroup CATALINA_THREAD_GROUP = new ThreadGroup("CatalinaContainer"); - - // load the JSSE properties (set the trust store) - static { - SSLUtil.loadJsseProperties(); - } - - private Tomcat tomcat = null; - protected Map<String, ContainerConfig.Configuration.Property> clusterConfig = new HashMap<String, ContainerConfig.Configuration.Property>(); - - protected boolean contextReloadable = false; - protected boolean crossContext = false; - protected boolean distribute = false; - - protected String catalinaRuntimeHome; private String name; + private Tomcat tomcat; @Override public void init(List<StartupCommand> ofbizCommands, String name, String configFile) throws ContainerException { - this.name = name; - // get the container config - ContainerConfig.Configuration cc = ContainerConfig.getConfiguration(name, configFile); - if (cc == null) { - throw new ContainerException("No catalina-container configuration found in container config!"); - } - // embedded properties - boolean useNaming = ContainerConfig.getPropertyValue(cc, "use-naming", false); - //int debug = ContainerConfig.getPropertyValue(cc, "debug", 0); - - // grab some global context settings - this.contextReloadable = ContainerConfig.getPropertyValue(cc, "apps-context-reloadable", false); - this.crossContext = ContainerConfig.getPropertyValue(cc, "apps-cross-context", true); - this.distribute = ContainerConfig.getPropertyValue(cc, "apps-distributable", true); + this.name = name; + ContainerConfig.Configuration configuration = ContainerConfig.getConfiguration(name, configFile); + Property engineConfig = retrieveTomcatEngineConfig(configuration); - this.catalinaRuntimeHome = ContainerConfig.getPropertyValue(cc, "catalina-runtime-home", "runtime/catalina"); + // tomcat setup + tomcat = prepareTomcatServer(configuration, engineConfig); + Engine engine = prepareTomcatEngine(tomcat, engineConfig); + Host host = prepareHost(tomcat, null); + + // clustering, valves and connectors setup + Property clusterProps = prepareTomcatClustering(host, engineConfig); + prepareTomcatEngineValves(engineConfig).forEach(valve -> ((StandardEngine)engine).addValve(valve)); + prepareTomcatConnectors(configuration).forEach(connector -> tomcat.getService().addConnector(connector)); - // set catalina_home - System.setProperty(Globals.CATALINA_HOME_PROP, System.getProperty("ofbiz.home") + "/" + this.catalinaRuntimeHome); - System.setProperty(Globals.CATALINA_BASE_PROP, System.getProperty(Globals.CATALINA_HOME_PROP)); + loadWebapps(tomcat, configuration, clusterProps); + } - // create the instance of embedded Tomcat - System.setProperty("catalina.useNaming", String.valueOf(useNaming)); - tomcat = new Tomcat(); - tomcat.setBaseDir(System.getProperty("ofbiz.home")); + public boolean start() throws ContainerException { + try { + tomcat.start(); + } catch (LifecycleException e) { + throw new ContainerException(e); + } - // configure JNDI in the StandardServer - StandardServer server = (StandardServer) tomcat.getServer(); - if (useNaming) { - tomcat.enableNaming(); + for (Connector con: tomcat.getService().findConnectors()) { + Debug.logInfo("Connector " + con.getProtocol() + " @ " + con.getPort() + " - " + + (con.getSecure() ? "secure" : "not-secure") + " [" + con.getProtocolHandlerClassName() + "] started.", module); } + Debug.logInfo("Started " + ServerInfo.getServerInfo(), module); + return true; + } + + public void stop() throws ContainerException { try { - server.setGlobalNamingContext(new InitialContext()); - } catch (NamingException e) { - throw new ContainerException(e); + tomcat.stop(); + } catch (LifecycleException e) { + /* Don't re-throw this exception or it will kill the rest of the shutdown process. + * Happens usually when running tests. Output disabled unless in verbose */ + Debug.logVerbose(e, module); } + } - // create the engine + public String getName() { + return name; + } + + private Property retrieveTomcatEngineConfig(ContainerConfig.Configuration cc) throws ContainerException { List<ContainerConfig.Configuration.Property> engineProps = cc.getPropertiesWithValue("engine"); if (UtilValidate.isEmpty(engineProps)) { throw new ContainerException("Cannot load CatalinaContainer; no engines defined."); @@ -204,53 +153,43 @@ public class CatalinaContainer implement if (engineProps.size() > 1) { throw new ContainerException("Cannot load CatalinaContainer; more than one engine configuration found; only one is supported."); } - createEngine(engineProps.get(0)); - - // create the connectors - List<ContainerConfig.Configuration.Property> connectorProps = cc.getPropertiesWithValue("connector"); - if (UtilValidate.isEmpty(connectorProps)) { - throw new ContainerException("Cannot load CatalinaContainer; no connectors defined!"); - } - for (ContainerConfig.Configuration.Property connectorProp: connectorProps) { - createConnector(connectorProp); - } + return engineProps.get(0); } - public boolean start() throws ContainerException { - // load the web applications - loadComponents(); + private Tomcat prepareTomcatServer(ContainerConfig.Configuration cc, + ContainerConfig.Configuration.Property engineConfig) throws ContainerException { - // Start the Tomcat server - try { - tomcat.getServer().start(); - } catch (LifecycleException e) { - throw new ContainerException(e); - } + System.setProperty(Globals.CATALINA_HOME_PROP, System.getProperty("ofbiz.home") + "/" + + ContainerConfig.getPropertyValue(cc, "catalina-runtime-home", "runtime/catalina")); + System.setProperty(Globals.CATALINA_BASE_PROP, System.getProperty(Globals.CATALINA_HOME_PROP)); - for (Connector con: tomcat.getService().findConnectors()) { - Debug.logInfo("Connector " + con.getProtocol() + " @ " + con.getPort() + " - " + - (con.getSecure() ? "secure" : "not-secure") + " [" + con.getProtocolHandlerClassName() + "] started.", module); + Tomcat tomcat = new Tomcat(); + tomcat.setBaseDir(System.getProperty("ofbiz.home")); + + Property defaultHostProp = engineConfig.getProperty("default-host"); + if (defaultHostProp == null) { + throw new ContainerException("default-host element of server property is required for catalina!"); + } else { + tomcat.setHostname(defaultHostProp.value); } - Debug.logInfo("Started " + ServerInfo.getServerInfo(), module); - return true; - } - private Engine createEngine(ContainerConfig.Configuration.Property engineConfig) throws ContainerException { - if (tomcat == null) { - throw new ContainerException("Cannot create Engine without Tomcat instance!"); + if (ContainerConfig.getPropertyValue(cc, "use-naming", false)) { + tomcat.enableNaming(); } - ContainerConfig.Configuration.Property defaultHostProp = engineConfig.getProperty("default-host"); - if (defaultHostProp == null) { - throw new ContainerException("default-host element of server property is required for catalina!"); + StandardServer server = (StandardServer) tomcat.getServer(); + try { + server.setGlobalNamingContext(new InitialContext()); + } catch (NamingException e) { + throw new ContainerException(e); } - String engineName = engineConfig.name; - String hostName = defaultHostProp.value; + return tomcat; + } - tomcat.setHostname(hostName); + private Engine prepareTomcatEngine(Tomcat tomcat, Property engineConfig) { Engine engine = tomcat.getEngine(); - engine.setName(engineName); + engine.setName(engineConfig.name); // set the JVM Route property (JK/JK2) String jvmRoute = ContainerConfig.getPropertyValue(engineConfig, "jvm-route", null); @@ -258,107 +197,125 @@ public class CatalinaContainer implement engine.setJvmRoute(jvmRoute); } - // create a default virtual host; others will be created as needed - Host host = tomcat.getHost(); - configureHost(host); + return engine; + } - // configure clustering - List<ContainerConfig.Configuration.Property> clusterProps = engineConfig.getPropertiesWithValue("cluster"); - if (clusterProps != null && clusterProps.size() > 1) { - throw new ContainerException("Only one cluster configuration allowed per engine"); - } + private Host prepareHost(Tomcat tomcat, List<String> virtualHosts) { + Host host; - if (UtilValidate.isNotEmpty(clusterProps)) { - ContainerConfig.Configuration.Property clusterProp = clusterProps.get(0); - createCluster(clusterProp, host); - clusterConfig.put(engineName, clusterProp); + if (UtilValidate.isEmpty(virtualHosts)) { + host = tomcat.getHost(); + } else { + host = prepareVirtualHost(tomcat, virtualHosts); } - // configure the CrossSubdomainSessionValve - boolean enableSessionValve = ContainerConfig.getPropertyValue(engineConfig, "enable-cross-subdomain-sessions", false); - if (enableSessionValve) { - CrossSubdomainSessionValve sessionValve = new CrossSubdomainSessionValve(); - ((StandardEngine)engine).addValve(sessionValve); - } + host.setAppBase(System.getProperty("ofbiz.home") + "/framework/catalina/hosts"); + host.setDeployOnStartup(false); + host.setBackgroundProcessorDelay(5); + host.setAutoDeploy(false); + ((StandardHost)host).setWorkDir(new File(System.getProperty(Globals.CATALINA_HOME_PROP), + "work" + File.separator + host.getName()).getAbsolutePath()); - // configure the access log valve - String logDir = ContainerConfig.getPropertyValue(engineConfig, "access-log-dir", null); - AccessLogValve al = null; - if (logDir != null) { - al = new AccessLogValve(); - if (!logDir.startsWith("/")) { - logDir = System.getProperty("ofbiz.home") + "/" + logDir; - } - File logFile = new File(logDir); - if (!logFile.isDirectory()) { - throw new ContainerException("Log directory [" + logDir + "] is not available; make sure the directory is created"); - } - al.setDirectory(logFile.getAbsolutePath()); - } + return host; + } - // configure the SslAcceleratorValve - String sslAcceleratorPortStr = ContainerConfig.getPropertyValue(engineConfig, "ssl-accelerator-port", null); - if (UtilValidate.isNotEmpty(sslAcceleratorPortStr)) { - Integer sslAcceleratorPort = Integer.valueOf(sslAcceleratorPortStr); - SslAcceleratorValve sslAcceleratorValve = new SslAcceleratorValve(); - sslAcceleratorValve.setSslAcceleratorPort(sslAcceleratorPort); - ((StandardEngine)engine).addValve(sslAcceleratorValve); + private Host prepareVirtualHost(Tomcat tomcat, List<String> virtualHosts) { + // assume that the first virtual-host will be the default; additional virtual-hosts will be aliases + String hostName = virtualHosts.get(0); + Host host; + Engine engine = tomcat.getEngine(); + + org.apache.catalina.Container childContainer = engine.findChild(hostName); + if (childContainer instanceof Host) { + host = (Host) childContainer; + } else { + host = new StandardHost(); + host.setName(hostName); + engine.addChild(host); } + virtualHosts.stream() + .filter(virtualHost -> virtualHost != hostName) + .forEach(virtualHost -> host.addAlias(virtualHost)); - String alp2 = ContainerConfig.getPropertyValue(engineConfig, "access-log-pattern", null); - if (al != null && UtilValidate.isNotEmpty(alp2)) { - al.setPattern(alp2); - } + return host; + } - String alp3 = ContainerConfig.getPropertyValue(engineConfig, "access-log-prefix", null); - if (al != null && UtilValidate.isNotEmpty(alp3)) { - al.setPrefix(alp3); + private Property prepareTomcatClustering(Host host, Property engineConfig) throws ContainerException { + Property clusterProp = null; + + List<Property> clusterProps = engineConfig.getPropertiesWithValue("cluster"); + if (clusterProps != null && clusterProps.size() > 1) { + throw new ContainerException("Only one cluster configuration allowed per engine"); } - boolean alp5 = ContainerConfig.getPropertyValue(engineConfig, "access-log-rotate", false); - if (al != null) { - al.setRotatable(alp5); + if (UtilValidate.isNotEmpty(clusterProps)) { + clusterProp = clusterProps.get(0); + + GroupChannel channel = new GroupChannel(); + channel.setChannelReceiver(prepareChannelReceiver(clusterProp)); + channel.setChannelSender(prepareChannelSender(clusterProp)); + channel.setMembershipService(prepareChannelMcastService(clusterProp)); + + SimpleTcpCluster cluster = new SimpleTcpCluster(); + cluster.setClusterName(clusterProp.name); + cluster.setManagerTemplate(prepareClusterManager(clusterProp)); + cluster.setChannel(channel); + cluster.addValve(prepareClusterValve(clusterProp)); + + host.setCluster(cluster); + + Debug.logInfo("Catalina Cluster [" + cluster.getClusterName() + "] configured for host - " + host.getName(), module); } + return clusterProp; + } + + private NioReceiver prepareChannelReceiver(Property clusterProp) throws ContainerException { + NioReceiver listener = new NioReceiver(); - if (al != null) { - ((StandardEngine)engine).addValve(al); + String tla = ContainerConfig.getPropertyValue(clusterProp, "tcp-listen-host", "auto"); + int tlp = ContainerConfig.getPropertyValue(clusterProp, "tcp-listen-port", 4001); + int tlt = ContainerConfig.getPropertyValue(clusterProp, "tcp-sector-timeout", 100); + int tlc = ContainerConfig.getPropertyValue(clusterProp, "tcp-thread-count", 6); + + if (tlp == -1) { + throw new ContainerException("Cluster configuration requires tcp-listen-port property"); } - return engine; - } + listener.setAddress(tla); + listener.setPort(tlp); + listener.setSelectorTimeout(tlt); + listener.setMaxThreads(tlc); + listener.setMinThreads(tlc); - private static Host createHost(String hostName) { - Host host = new StandardHost(); - host.setName(hostName); - configureHost(host); - return host; - } - private static void configureHost(Host host) { - host.setAppBase(CATALINA_HOSTS_HOME); - host.setDeployOnStartup(false); - host.setBackgroundProcessorDelay(5); - host.setAutoDeploy(false); - ((StandardHost)host).setWorkDir(new File(System.getProperty(Globals.CATALINA_HOME_PROP), "work" + File.separator + host.getName()).getAbsolutePath()); + return listener; } - protected Cluster createCluster(ContainerConfig.Configuration.Property clusterProps, Host host) throws ContainerException { - String defaultValveFilter = ".*\\.gif;.*\\.js;.*\\.jpg;.*\\.htm;.*\\.html;.*\\.txt;.*\\.png;.*\\.css;.*\\.ico;.*\\.htc;"; + private ReplicationTransmitter prepareChannelSender(Property clusterProp) throws ContainerException { + ReplicationTransmitter trans = new ReplicationTransmitter(); + try { + MultiPointSender mps = (MultiPointSender)Class.forName(ContainerConfig.getPropertyValue(clusterProp, + "replication-mode", "org.apache.catalina.tribes.transport.bio.PooledMultiSender")).newInstance(); + trans.setTransport(mps); + } catch (Exception exc) { + throw new ContainerException("Cluster configuration requires a valid replication-mode property: " + exc.getMessage()); + } + return trans; + } - ReplicationValve clusterValve = new ReplicationValve(); - clusterValve.setFilter(ContainerConfig.getPropertyValue(clusterProps, "rep-valve-filter", defaultValveFilter)); + private McastService prepareChannelMcastService(Property clusterProp) throws ContainerException { + McastService mcast = new McastService(); - String mcb = ContainerConfig.getPropertyValue(clusterProps, "mcast-bind-addr", null); - String mca = ContainerConfig.getPropertyValue(clusterProps, "mcast-addr", null); - int mcp = ContainerConfig.getPropertyValue(clusterProps, "mcast-port", -1); - int mcf = ContainerConfig.getPropertyValue(clusterProps, "mcast-freq", 500); - int mcd = ContainerConfig.getPropertyValue(clusterProps, "mcast-drop-time", 3000); + String mcb = ContainerConfig.getPropertyValue(clusterProp, "mcast-bind-addr", null); + String mca = ContainerConfig.getPropertyValue(clusterProp, "mcast-addr", null); + int mcp = ContainerConfig.getPropertyValue(clusterProp, "mcast-port", -1); + int mcf = ContainerConfig.getPropertyValue(clusterProp, "mcast-freq", 500); + int mcd = ContainerConfig.getPropertyValue(clusterProp, "mcast-drop-time", 3000); if (mca == null || mcp == -1) { throw new ContainerException("Cluster configuration requires mcast-addr and mcast-port properties"); } - McastService mcast = new McastService(); if (mcb != null) { mcast.setMcastBindAddress(mcb); } @@ -368,86 +325,88 @@ public class CatalinaContainer implement mcast.setMcastDropTime(mcd); mcast.setFrequency(mcf); - String tla = ContainerConfig.getPropertyValue(clusterProps, "tcp-listen-host", "auto"); - int tlp = ContainerConfig.getPropertyValue(clusterProps, "tcp-listen-port", 4001); - int tlt = ContainerConfig.getPropertyValue(clusterProps, "tcp-sector-timeout", 100); - int tlc = ContainerConfig.getPropertyValue(clusterProps, "tcp-thread-count", 6); - //String tls = getPropertyValue(clusterProps, "", ""); + return mcast; + } - if (tlp == -1) { - throw new ContainerException("Cluster configuration requires tcp-listen-port property"); + private ClusterManager prepareClusterManager(Property clusterProp) throws ContainerException { + String mgrClassName = ContainerConfig.getPropertyValue(clusterProp, "manager-class", "org.apache.catalina.ha.session.DeltaManager"); + try { + return (ClusterManager)Class.forName(mgrClassName).newInstance(); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { + throw new ContainerException("Cluster configuration requires a valid manager-class property", e); } + } - NioReceiver listener = new NioReceiver(); - listener.setAddress(tla); - listener.setPort(tlp); - listener.setSelectorTimeout(tlt); - listener.setMaxThreads(tlc); - listener.setMinThreads(tlc); - //listener.setIsSenderSynchronized(false); + private ReplicationValve prepareClusterValve(Property clusterProp) { + ReplicationValve clusterValve = new ReplicationValve(); + String defaultValveFilter = ".*\\.gif;.*\\.js;.*\\.jpg;.*\\.htm;.*\\.html;.*\\.txt;.*\\.png;.*\\.css;.*\\.ico;.*\\.htc;"; + clusterValve.setFilter(ContainerConfig.getPropertyValue(clusterProp, "rep-valve-filter", defaultValveFilter)); + return clusterValve; + } - ReplicationTransmitter trans = new ReplicationTransmitter(); - try { - MultiPointSender mps = (MultiPointSender)Class.forName(ContainerConfig.getPropertyValue(clusterProps, "replication-mode", "org.apache.catalina.tribes.transport.bio.PooledMultiSender")).newInstance(); - trans.setTransport(mps); - } catch (Exception exc) { - throw new ContainerException("Cluster configuration requires a valid replication-mode property: " + exc.getMessage()); + private List<Valve> prepareTomcatEngineValves(Property engineConfig) throws ContainerException { + List<Valve> engineValves = new ArrayList<Valve>(); + + // configure the CrossSubdomainSessionValve + if (ContainerConfig.getPropertyValue(engineConfig, "enable-cross-subdomain-sessions", false)) { + engineValves.add(new CrossSubdomainSessionValve()); } - String mgrClassName = ContainerConfig.getPropertyValue(clusterProps, "manager-class", "org.apache.catalina.ha.session.DeltaManager"); - //int debug = ContainerConfig.getPropertyValue(clusterProps, "debug", 0); - // removed since 5.5.9? boolean expireSession = ContainerConfig.getPropertyValue(clusterProps, "expire-session", false); - // removed since 5.5.9? boolean useDirty = ContainerConfig.getPropertyValue(clusterProps, "use-dirty", true); - - SimpleTcpCluster cluster = new SimpleTcpCluster(); - cluster.setClusterName(clusterProps.name); - Manager manager = null; - try { - manager = (Manager)Class.forName(mgrClassName).newInstance(); - } catch (Exception exc) { - throw new ContainerException("Cluster configuration requires a valid manager-class property: " + exc.getMessage()); + + // configure the SslAcceleratorValve + String sslAcceleratorPortStr = ContainerConfig.getPropertyValue(engineConfig, "ssl-accelerator-port", null); + if (UtilValidate.isNotEmpty(sslAcceleratorPortStr)) { + Integer sslAcceleratorPort = Integer.valueOf(sslAcceleratorPortStr); + SslAcceleratorValve sslAcceleratorValve = new SslAcceleratorValve(); + sslAcceleratorValve.setSslAcceleratorPort(sslAcceleratorPort); + engineValves.add(sslAcceleratorValve); } - //cluster.setManagerClassName(mgrClassName); - //host.setManager(manager); - //cluster.registerManager(manager); - cluster.setManagerTemplate((org.apache.catalina.ha.ClusterManager)manager); - //cluster.setDebug(debug); - // removed since 5.5.9? cluster.setExpireSessionsOnShutdown(expireSession); - // removed since 5.5.9? cluster.setUseDirtyFlag(useDirty); - - GroupChannel channel = new GroupChannel(); - channel.setChannelReceiver(listener); - channel.setChannelSender(trans); - channel.setMembershipService(mcast); - - cluster.setChannel(channel); - cluster.addValve(clusterValve); - // removed since 5.5.9? cluster.setPrintToScreen(true); - - // set the cluster to the host - host.setCluster(cluster); - Debug.logInfo("Catalina Cluster [" + cluster.getClusterName() + "] configured for host - " + host.getName(), module); - - return cluster; - } - - protected Connector createConnector(ContainerConfig.Configuration.Property connectorProp) throws ContainerException { - if (tomcat == null) { - throw new ContainerException("Cannot create Connector without Tomcat instance!"); - } - Connector connector = null; - if (UtilValidate.isNotEmpty(connectorProp.properties)) { - String protocol = ContainerConfig.getPropertyValue(connectorProp, "protocol", "HTTP/1.1"); - int port = ContainerConfig.getPropertyValue(connectorProp, "port", 0) + Start.getInstance().getConfig().portOffset; - - // set the protocol and the port first - connector = new Connector(protocol); - connector.setPort(port); - // then set all the other parameters - for (ContainerConfig.Configuration.Property prop: connectorProp.properties.values()) { - if ("protocol".equals(prop.name) || "port".equals(prop.name)) { - // protocol and port are already set - continue; - } + + // configure the AccessLogValve + String logDir = ContainerConfig.getPropertyValue(engineConfig, "access-log-dir", null); + if (logDir != null) { + AccessLogValve accessLogValve = new AccessLogValve(); + + logDir = logDir.startsWith("/") ? System.getProperty("ofbiz.home") + "/" + logDir : logDir; + File logFile = new File(logDir); + if (!logFile.isDirectory()) { + throw new ContainerException("Log directory [" + logDir + "] is not available; make sure the directory is created"); + } + + accessLogValve.setDirectory(logFile.getAbsolutePath()); + String accessLogPattern = ContainerConfig.getPropertyValue(engineConfig, "access-log-pattern", null); + if (UtilValidate.isNotEmpty(accessLogPattern)) { + accessLogValve.setPattern(accessLogPattern); + } + String accessLogPrefix = ContainerConfig.getPropertyValue(engineConfig, "access-log-prefix", null); + if (UtilValidate.isNotEmpty(accessLogPrefix)) { + accessLogValve.setPrefix(accessLogPrefix); + } + accessLogValve.setRotatable(ContainerConfig.getPropertyValue(engineConfig, "access-log-rotate", false)); + + engineValves.add(accessLogValve); + } + + return engineValves; + } + + private List<Connector> prepareTomcatConnectors(Configuration configuration) throws ContainerException { + List<Property> connectorProps = configuration.getPropertiesWithValue("connector"); + if (UtilValidate.isEmpty(connectorProps)) { + throw new ContainerException("Cannot load CatalinaContainer; no connectors defined!"); + } + return connectorProps.stream() + .filter(connectorProp -> UtilValidate.isNotEmpty(connectorProp.properties)) + .map(connectorProp -> prepareConnector(connectorProp)) + .collect(Collectors.toList()); + } + + private Connector prepareConnector(Property connectorProp) { + Connector connector = new Connector(ContainerConfig.getPropertyValue(connectorProp, "protocol", "HTTP/1.1")); + connector.setPort(ContainerConfig.getPropertyValue(connectorProp, "port", 0) + Start.getInstance().getConfig().portOffset); + + connectorProp.properties.values().stream() + .filter(prop -> !"protocol".equals(prop.name) && !"port".equals(prop.name)) + .forEach(prop -> { if (IntrospectionUtils.setProperty(connector, prop.name, prop.value)) { if (prop.name.indexOf("Pass") != -1) { // this property may be a password, do not include its value in the logs @@ -458,106 +417,99 @@ public class CatalinaContainer implement } else { Debug.logWarning("Tomcat " + connector + ": ignored parameter " + prop.name, module); } - } - - tomcat.getService().addConnector(connector); - } + }); return connector; } - private Callable<Context> createContext(final ComponentConfig.WebappInfo appInfo) throws ContainerException { - Debug.logInfo("Creating context [" + appInfo.name + "]", module); - final Engine engine = tomcat.getEngine(); + private void loadWebapps(Tomcat tomcat, ContainerConfig.Configuration configuration, Property clusterProp) { + ScheduledExecutorService executor = ExecutionPool.getScheduledExecutor(new ThreadGroup(module), + "catalina-startup", Runtime.getRuntime().availableProcessors(), 0, true); + List<Future<Context>> futures = new ArrayList<Future<Context>>(); - List<String> virtualHosts = appInfo.getVirtualHosts(); - final Host host; - if (UtilValidate.isEmpty(virtualHosts)) { - host = tomcat.getHost(); - } else { - // assume that the first virtual-host will be the default; additional virtual-hosts will be aliases - Iterator<String> vhi = virtualHosts.iterator(); - String hostName = vhi.next(); - - org.apache.catalina.Container childContainer = engine.findChild(hostName); - if (childContainer instanceof Host) { - host = (Host)childContainer; + List<ComponentConfig.WebappInfo> webResourceInfos = ComponentConfig.getAllWebappResourceInfos(); + Collections.reverse(webResourceInfos); // allow higher level webapps to override lower ones + + Set<String> webappsMounts = new HashSet<String>(); + webResourceInfos.forEach(appInfo -> webappsMounts.addAll(getWebappMounts(appInfo))); + + for (ComponentConfig.WebappInfo appInfo: webResourceInfos) { + if(webappsMounts.removeAll(getWebappMounts(appInfo))) { + // webapp is not yet loaded + if (!appInfo.location.isEmpty()) { + futures.add(executor.submit(createCallableContext(tomcat, appInfo, clusterProp, configuration))); + } } else { - host = createHost(hostName); - engine.addChild(host); - } - while (vhi.hasNext()) { - host.addAlias(vhi.next()); + /* webapp is loaded already (overridden). Therefore, disable + * app bar display on overridden apps and do not load */ + appInfo.setAppBarDisplay(false); + Debug.logInfo("Duplicate webapp mount; not loading : " + appInfo.getName() + " / " + appInfo.getLocation(), module); } } + ExecutionPool.getAllFutures(futures); + executor.shutdown(); + } + + private List<String> getWebappMounts(ComponentConfig.WebappInfo webappInfo) { + List<String> allAppsMounts = new ArrayList<String>(); + String engineName = webappInfo.server; + String mount = webappInfo.getContextRoot(); + List<String> virtualHosts = webappInfo.getVirtualHosts(); + if (virtualHosts.isEmpty()) { + allAppsMounts.add(engineName + ":DEFAULT:" + mount); + } else { + virtualHosts.forEach(virtualHost -> allAppsMounts.add(engineName + ":" + virtualHost + ":" + mount)); + } + return allAppsMounts; + } + + private Callable<Context> createCallableContext(Tomcat tomcat, ComponentConfig.WebappInfo appInfo, + Property clusterProp, ContainerConfig.Configuration configuration) { + + Debug.logInfo("Creating context [" + appInfo.name + "]", module); + Host host = prepareHost(tomcat, appInfo.getVirtualHosts()); + return new Callable<Context>() { public Context call() throws ContainerException, LifecycleException { - StandardContext context = configureContext(engine, host, appInfo); + StandardContext context = prepareContext(host, configuration, appInfo, clusterProp); host.addChild(context); return context; } }; } - private StandardContext configureContext(Engine engine, Host host, ComponentConfig.WebappInfo appInfo) throws ContainerException { - // webapp settings - Map<String, String> initParameters = appInfo.getInitParameters(); + private StandardContext prepareContext(Host host, ContainerConfig.Configuration configuration, + ComponentConfig.WebappInfo appInfo, Property clusterProp) throws ContainerException { - // set the root location (make sure we set the paths correctly) - String location = appInfo.componentConfig.getRootLocation() + appInfo.location; - location = location.replace('\\', '/'); - if (location.endsWith("/")) { - location = location.substring(0, location.length() - 1); - } - - // get the mount point - String mount = appInfo.mountPoint; - if (mount.endsWith("/*")) { - mount = mount.substring(0, mount.length() - 2); - } + StandardContext context = new StandardContext(); + Tomcat.initWebappDefaults(context); - final String webXmlFilePath = new StringBuilder().append("file:///").append(location).append("/WEB-INF/web.xml").toString(); - boolean appIsDistributable = distribute; - URL webXmlUrl = null; - try { - webXmlUrl = FlexibleLocation.resolveLocation(webXmlFilePath); - } catch (MalformedURLException e) { - throw new ContainerException(e); - } - File webXmlFile = new File(webXmlUrl.getFile()); - if (webXmlFile.exists()) { - Document webXmlDoc = null; - try { - webXmlDoc = UtilXml.readXmlDocument(webXmlUrl); - } catch (Exception e) { - throw new ContainerException(e); - } - appIsDistributable = webXmlDoc.getElementsByTagName("distributable").getLength() > 0; - } else { - Debug.logInfo(webXmlFilePath + " not found.", module); - } - final boolean contextIsDistributable = distribute && appIsDistributable; + String location = getWebappRootLocation(appInfo); + boolean contextIsDistributable = isContextDistributable(configuration, location); - // create the web application context - StandardContext context = new StandardContext(); context.setParent(host); context.setDocBase(location); - context.setPath(mount); + context.setPath(getWebappMountPoint(appInfo)); context.addLifecycleListener(new ContextConfig()); - Tomcat.initWebappDefaults(context); - // configure persistent sessions - // important: the call to context.setManager(...) must be done after Tomcat.initWebappDefaults(...) - Property clusterProp = clusterConfig.get(engine.getName()); + context.setJ2EEApplication("OFBiz"); + context.setJ2EEServer("OFBiz Container"); + context.setLoader(new WebappLoader(Thread.currentThread().getContextClassLoader())); + context.setDisplayName(appInfo.name); + context.setDocBase(location); + context.setReloadable(ContainerConfig.getPropertyValue(configuration, "apps-context-reloadable", false)); + context.setDistributable(contextIsDistributable); + context.setCrossContext(ContainerConfig.getPropertyValue(configuration, "apps-cross-context", true)); + context.setPrivileged(appInfo.privileged); + context.getServletContext().setAttribute("_serverId", appInfo.server); + context.getServletContext().setAttribute("componentName", appInfo.componentConfig.getComponentName()); + if (clusterProp != null && contextIsDistributable) { - Manager sessionMgr = null; - String mgrClassName = ContainerConfig.getPropertyValue(clusterProp, "manager-class", "org.apache.catalina.ha.session.DeltaManager"); - try { - sessionMgr = (Manager)Class.forName(mgrClassName).newInstance(); - } catch (Exception exc) { - throw new ContainerException("Cluster configuration requires a valid manager-class property: " + exc.getMessage()); - } - context.setManager(sessionMgr); + context.setManager(prepareClusterManager(clusterProp)); } + StandardRoot resources = new StandardRoot(context); + resources.setAllowLinking(true); + context.setResources(resources); + JarScanner jarScanner = context.getJarScanner(); if (jarScanner instanceof StandardJarScanner) { StandardJarScanner standardJarScanner = (StandardJarScanner) jarScanner; @@ -565,33 +517,14 @@ public class CatalinaContainer implement standardJarScanner.setScanClassPath(true); } - context.setJ2EEApplication(J2EE_APP); - context.setJ2EEServer(J2EE_SERVER); - context.setLoader(new WebappLoader(Thread.currentThread().getContextClassLoader())); - - context.setDisplayName(appInfo.name); - context.setDocBase(location); - - StandardRoot resources = new StandardRoot(context); - resources.setAllowLinking(true); - context.setResources(resources); - - context.setReloadable(contextReloadable); - - context.setDistributable(contextIsDistributable); - - context.setCrossContext(crossContext); - context.setPrivileged(appInfo.privileged); - context.getServletContext().setAttribute("_serverId", appInfo.server); - context.getServletContext().setAttribute("componentName", appInfo.componentConfig.getComponentName()); - + Map<String, String> initParameters = appInfo.getInitParameters(); // request dumper filter - String enableRequestDump = initParameters.get("enableRequestDump"); - if ("true".equals(enableRequestDump)) { - // create the Requester Dumper Filter instance + if ("true".equals(initParameters.get("enableRequestDump"))) { FilterDef requestDumperFilterDef = new FilterDef(); requestDumperFilterDef.setFilterClass(RequestDumperFilter.class.getName()); requestDumperFilterDef.setFilterName("RequestDumper"); + context.addFilterDef(requestDumperFilterDef); + FilterMap requestDumperFilterMap = new FilterMap(); requestDumperFilterMap.setFilterName("RequestDumper"); requestDumperFilterMap.addURLPattern("*"); @@ -599,71 +532,43 @@ public class CatalinaContainer implement } // set the init parameters - for (Map.Entry<String, String> entry: initParameters.entrySet()) { - context.addParameter(entry.getKey(), entry.getValue()); - } + initParameters.entrySet().forEach(entry -> context.addParameter(entry.getKey(), entry.getValue())); return context; } - protected void loadComponents() throws ContainerException { - if (tomcat == null) { - throw new ContainerException("Cannot load web applications without Tomcat instance!"); - } - - // load the applications - List<ComponentConfig.WebappInfo> webResourceInfos = ComponentConfig.getAllWebappResourceInfos(); - List<String> loadedMounts = new ArrayList<String>(); - if (webResourceInfos == null) { - return; + private String getWebappRootLocation(ComponentConfig.WebappInfo appInfo) { + String location = appInfo.componentConfig.getRootLocation() + appInfo.location; + location = location.replace('\\', '/'); + if (location.endsWith("/")) { + location = location.substring(0, location.length() - 1); } + return location; + } - ScheduledExecutorService executor = ExecutionPool.getScheduledExecutor(CATALINA_THREAD_GROUP, "catalina-startup", Runtime.getRuntime().availableProcessors(), 0, true); - try { - List<Future<Context>> futures = new ArrayList<Future<Context>>(); - - for (int i = webResourceInfos.size(); i > 0; i--) { - ComponentConfig.WebappInfo appInfo = webResourceInfos.get(i - 1); - String engineName = appInfo.server; - List<String> virtualHosts = appInfo.getVirtualHosts(); - String mount = appInfo.getContextRoot(); - List<String> keys = new ArrayList<String>(); - if (virtualHosts.isEmpty()) { - keys.add(engineName + ":DEFAULT:" + mount); - } else { - for (String virtualHost: virtualHosts) { - keys.add(engineName + ":" + virtualHost + ":" + mount); - } - } - if (!keys.removeAll(loadedMounts)) { - // nothing was removed from the new list of keys; this - // means there are no existing loaded entries that overlap - // with the new set - if (!appInfo.location.isEmpty()) { - futures.add(executor.submit(createContext(appInfo))); - } - loadedMounts.addAll(keys); - } else { - appInfo.setAppBarDisplay(false); // disable app bar display on overridden apps - Debug.logInfo("Duplicate webapp mount; not loading : " + appInfo.getName() + " / " + appInfo.getLocation(), module); - } - } - ExecutionPool.getAllFutures(futures); - } finally { - executor.shutdown(); + private String getWebappMountPoint(ComponentConfig.WebappInfo appInfo) { + String mount = appInfo.mountPoint; + if (mount.endsWith("/*")) { + mount = mount.substring(0, mount.length() - 2); } + return mount; } - public void stop() throws ContainerException { + private boolean isContextDistributable(ContainerConfig.Configuration configuration, String location) throws ContainerException { + String webXmlFilePath = new StringBuilder().append("file:///").append(location).append("/WEB-INF/web.xml").toString(); + boolean appIsDistributable = ContainerConfig.getPropertyValue(configuration, "apps-distributable", true); try { - tomcat.stop(); - } catch (LifecycleException e) { - // don't throw this; or it will kill the rest of the shutdown process - Debug.logVerbose(e, module); // happens usually when running tests, disabled unless in verbose + URL webXmlUrl = FlexibleLocation.resolveLocation(webXmlFilePath); + File webXmlFile = new File(webXmlUrl.getFile()); + if (webXmlFile.exists()) { + Document webXmlDoc = UtilXml.readXmlDocument(webXmlUrl); + return appIsDistributable && webXmlDoc.getElementsByTagName("distributable").getLength() > 0; + } else { + Debug.logInfo(webXmlFilePath + " not found.", module); + return appIsDistributable; + } + } catch (SAXException | ParserConfigurationException | IOException e) { + throw new ContainerException(e); } } - - public String getName() { - return name; - } } |
Free forum by Nabble | Edit this page |