| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license |
| * agreements. See the NOTICE file distributed with this work for additional information regarding |
| * copyright ownership. The ASF licenses this file to You 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.apache.geode.session.tests; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.file.Files; |
| import java.nio.file.StandardCopyOption; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.function.IntSupplier; |
| import java.util.regex.Pattern; |
| |
| |
| /** |
| * Tomcat specific container installation class |
| * |
| * Provides logic for installation of tomcat. This makes the modifications to the tomcat install as |
| * described in <a href= |
| * "https://geode.apache.org/docs/guide/latest/tools_modules/http_session_mgmt/session_mgmt_tomcat.html"> |
| * the geode docs</a>. |
| */ |
| public class TomcatInstall extends ContainerInstall { |
| /** |
| * Version of tomcat that this class will install |
| * |
| * Includes the download URL for the each version, the version number associated with each |
| * version, and other properties or XML attributes needed to setup tomcat containers within Cargo |
| */ |
| public enum TomcatVersion { |
| TOMCAT6(6, "tomcat-6.0.37.zip"), |
| TOMCAT7(7, "tomcat-7.0.90.zip"), |
| TOMCAT8(8, "tomcat-8.5.34.zip"), |
| TOMCAT9(9, "tomcat-9.0.12.zip"); |
| |
| private final int version; |
| |
| private final String downloadURL; |
| |
| TomcatVersion(int version, String downloadURL) { |
| this.version = version; |
| this.downloadURL = downloadURL; |
| } |
| |
| /** |
| * Converts the version to an integer |
| * |
| */ |
| public int toInteger() { |
| return getVersion(); |
| } |
| |
| public int getVersion() { |
| return version; |
| } |
| |
| public String getContainerId() { |
| return "tomcat" + getVersion() + "x"; |
| } |
| |
| public String getDownloadURL() { |
| return downloadURL; |
| } |
| |
| public String jarSkipPropertyName() { |
| switch (this) { |
| case TOMCAT6: |
| return null; |
| case TOMCAT7: |
| return "tomcat.util.scan.DefaultJarScanner.jarsToSkip"; |
| case TOMCAT8: |
| case TOMCAT9: |
| return "tomcat.util.scan.StandardJarScanFilter.jarsToSkip"; |
| default: |
| throw new IllegalArgumentException("Illegal tomcat version option"); |
| } |
| } |
| } |
| |
| /** |
| * If you update this list method to return different dependencies, please also update |
| * the Tomcat module documentation! |
| * The documentation can be found here: |
| * geode-docs/tools_modules/http_session_mgmt/tomcat_installing_the_module.html.md.erb |
| */ |
| private static final String[] tomcatRequiredJars = |
| {"antlr", "commons-io", "commons-lang", "commons-validator", "fastutil", "geode-common", |
| "geode-core", "geode-log4j", "geode-logging", "geode-management", "geode-serialization", |
| "javax.transaction-api", "jgroups", "log4j-api", "log4j-core", "log4j-jul", "micrometer", |
| "shiro-core", "jetty-server", "jetty-util", "jetty-http", "jetty-io"}; |
| |
| private final TomcatVersion version; |
| |
| public TomcatInstall(String name, TomcatVersion version, ConnectionType connectionType, |
| IntSupplier portSupplier) |
| throws Exception { |
| this(name, version, connectionType, DEFAULT_MODULE_LOCATION, GEODE_BUILD_HOME_LIB, |
| portSupplier); |
| } |
| |
| /** |
| * Download and setup an installation tomcat using the {@link ContainerInstall} constructor and |
| * some extra functions this class provides |
| * |
| * Specifically, this function uses {@link #copyTomcatGeodeReqFiles(String, String)} to install |
| * geode session into Tomcat, {@link #setupDefaultSettings()} to modify the context and server XML |
| * files within the installation's 'conf' folder, and {@link #updateProperties()} to set the jar |
| * skipping properties needed to speedup container startup. |
| */ |
| public TomcatInstall(String name, TomcatVersion version, ConnectionType connType, |
| String modulesJarLocation, String extraJarsPath, IntSupplier portSupplier) throws Exception { |
| // Does download and install from URL |
| super(name, version.getDownloadURL(), connType, "tomcat", modulesJarLocation, portSupplier); |
| |
| this.version = version; |
| modulesJarLocation = getModulePath() + "/lib/"; |
| |
| // Install geode sessions into tomcat install |
| copyTomcatGeodeReqFiles(modulesJarLocation, extraJarsPath); |
| // Set some default XML attributes in server and cache XMLs |
| setupDefaultSettings(); |
| |
| // Add required jars copied to jar skips so container startup is faster |
| if (version.jarSkipPropertyName() != null) { |
| updateProperties(); |
| } |
| } |
| |
| /** |
| * Modifies the context and server XML files in the installation's 'conf' directory so that they |
| * contain the session manager class ({@link #getContextSessionManagerClass()}) and life cycle |
| * listener class ({@link #getServerLifeCycleListenerClass()}) respectively |
| */ |
| public void setupDefaultSettings() { |
| HashMap<String, String> attributes = new HashMap<>(); |
| |
| // Set the session manager class within the context XML file |
| attributes.put("className", getContextSessionManagerClass()); |
| editXMLFile(getDefaultContextXMLFile().getAbsolutePath(), "Tomcat", "Manager", "Context", |
| attributes); |
| |
| // Set the server lifecycle listener within the server XML file |
| attributes.put("className", getServerLifeCycleListenerClass()); |
| editXMLFile(getDefaultServerXMLFile().getAbsolutePath(), "Tomcat", "Listener", "Server", |
| attributes); |
| } |
| |
| /** |
| * Get the server life cycle class that should be used |
| * |
| * Generates the class based on whether the installation's connection type |
| * {@link ContainerInstall#connType} is client server or peer to peer. |
| */ |
| public String getServerLifeCycleListenerClass() { |
| String className = "org.apache.geode.modules.session.catalina."; |
| switch (getConnectionType()) { |
| case PEER_TO_PEER: |
| className += "PeerToPeer"; |
| break; |
| case CLIENT_SERVER: |
| className += "ClientServer"; |
| break; |
| default: |
| throw new IllegalArgumentException( |
| "Bad connection type. Must be either PEER_TO_PEER or CLIENT_SERVER"); |
| } |
| |
| className += "CacheLifecycleListener"; |
| return className; |
| } |
| |
| /** |
| * Location of the context XML file in the installation's 'conf' directory |
| */ |
| public File getDefaultContextXMLFile() { |
| return new File(getHome() + "/conf/context.xml"); |
| } |
| |
| /** |
| * Location of the server XML file in the installation's 'conf' directory |
| */ |
| public File getDefaultServerXMLFile() { |
| return new File(getHome() + "/conf/server.xml"); |
| } |
| |
| /** |
| * Implements {@link ContainerInstall#getContextSessionManagerClass()} |
| * |
| * Gets the TomcatDeltaSessionManager class associated with this {@link #version}. Use's the |
| * {@link #version}'s toInteger function to do so. |
| */ |
| @Override |
| public String getContextSessionManagerClass() { |
| return "org.apache.geode.modules.session.catalina.Tomcat" + version.toInteger() |
| + "DeltaSessionManager"; |
| } |
| |
| /** |
| * Implementation of {@link ContainerInstall#generateContainer(File, String)}, which generates a |
| * Tomcat specific container |
| * |
| * Creates a {@link TomcatContainer} instance off of this installation. |
| * |
| * @param containerDescriptors Additional descriptors used to identify a container |
| */ |
| @Override |
| public TomcatContainer generateContainer(File containerConfigHome, String containerDescriptors) |
| throws IOException { |
| return new TomcatContainer(this, containerConfigHome, containerDescriptors, portSupplier()); |
| } |
| |
| /** |
| * The cargo specific installation id needed to setup a cargo container |
| * |
| * Based on the installation's {@link #version}. |
| */ |
| @Override |
| public String getInstallId() { |
| return version.getContainerId(); |
| } |
| |
| /** |
| * @see ContainerInstall#getInstallDescription() |
| */ |
| @Override |
| public String getInstallDescription() { |
| return version.name() + "_" + getConnectionType().getName(); |
| } |
| |
| /** |
| * Copies jars specified by {@link #tomcatRequiredJars} from the {@link #getModulePath()} and the |
| * specified other directory passed to the function |
| * |
| * @throws IOException if the {@link #getModulePath()}, installation lib directory, or extra |
| * directory passed in contain no files. |
| */ |
| private void copyTomcatGeodeReqFiles(String moduleJarDir, String extraJarsPath) |
| throws IOException { |
| ArrayList<File> requiredFiles = new ArrayList<>(); |
| // The library path for the current tomcat installation |
| String tomcatLibPath = getHome() + "/lib/"; |
| |
| // List of required jars and form version regexps from them |
| String versionRegex = "-?[0-9]*.*\\.jar"; |
| ArrayList<Pattern> patterns = new ArrayList<>(tomcatRequiredJars.length); |
| for (String jar : tomcatRequiredJars) |
| patterns.add(Pattern.compile(jar + versionRegex)); |
| |
| // Don't need to copy any jars already in the tomcat install |
| File tomcatLib = new File(tomcatLibPath); |
| |
| // Find all jars in the tomcatModulePath and add them as required jars |
| try { |
| for (File file : (new File(moduleJarDir)).listFiles()) { |
| if (file.isFile() && file.getName().endsWith(".jar")) { |
| requiredFiles.add(file); |
| } |
| } |
| } catch (NullPointerException e) { |
| throw new IOException( |
| "No files found in tomcat module directory " + getModulePath() + "/lib/"); |
| } |
| |
| // Find all the required jars in the extraJarsPath |
| try { |
| for (File file : (new File(extraJarsPath)).listFiles()) { |
| for (Pattern pattern : patterns) { |
| if (pattern.matcher(file.getName()).find()) { |
| requiredFiles.add(file); |
| break; |
| } |
| } |
| } |
| } catch (NullPointerException e) { |
| throw new IOException("No files found in extra jars directory " + extraJarsPath); |
| } |
| |
| // Copy the required jars to the given tomcat lib folder |
| for (File file : requiredFiles) { |
| Files.copy(file.toPath(), tomcatLib.toPath().resolve(file.toPath().getFileName()), |
| StandardCopyOption.REPLACE_EXISTING); |
| logger.debug("Copied required jar from " + file.toPath() + " to " |
| + (new File(tomcatLibPath)).toPath().resolve(file.toPath().getFileName())); |
| } |
| |
| logger.info("Copied required jars into the Tomcat installation"); |
| } |
| |
| /** |
| * Update the tomcat installation property file using {@link #editPropertyFile)} |
| */ |
| private void updateProperties() throws Exception { |
| String jarsToSkip = ""; |
| // Adds all the required jars as jars to skip when starting Tomcat |
| for (String jarName : tomcatRequiredJars) |
| jarsToSkip += "," + jarName + "*.jar"; |
| |
| // Add the jars to skip to the catalina property file |
| editPropertyFile(getHome() + "/conf/catalina.properties", version.jarSkipPropertyName(), |
| jarsToSkip, true); |
| } |
| } |