ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/catalina/ha/deploy/FarmWarDeployer.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/catalina/ha/deploy/FarmWarDeployer.java
Status
scanned
Type
file
Name
FarmWarDeployer.java
Extension
.java
Programming language
Java
Mime type
text/plain
File type
ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
26059 (25.4 KB)
MD5
790470da18e34721e5696f958e41b555
SHA1
77d1b287eb8d6f085e96e0789bfe85f504f50e11
SHA256
fd953e1db8465c80386883e4d4061b7fd687504296ffbf684068332479d955b8
SHA512

      
    
SHA1_git
77aef418ef81f6b031d5b5e63aec21cd05b37177
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
FarmWarDeployer.java | 25.4 KB |

/* * 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.catalina.ha.deploy; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.util.HashMap; import javax.management.MBeanServer; import javax.management.ObjectName; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Engine; import org.apache.catalina.Host; import org.apache.catalina.LifecycleException; import org.apache.catalina.ha.ClusterDeployer; import org.apache.catalina.ha.ClusterListener; import org.apache.catalina.ha.ClusterMessage; import org.apache.catalina.tribes.Member; import org.apache.catalina.util.ContextName; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.modeler.Registry; import org.apache.tomcat.util.res.StringManager; /** * <p> * A farm war deployer is a class that is able to deploy/undeploy web applications in WAR from within the cluster. * </p> * Any host can act as the admin, and will have three directories * <ul> * <li>watchDir - the directory where we watch for changes</li> * <li>deployDir - the directory where we install applications</li> * <li>tempDir - a temporaryDirectory to store binary data when downloading a war from the cluster</li> * </ul> * Currently we only support deployment of WAR files since they are easier to send across the wire. */ public class FarmWarDeployer extends ClusterListener implements ClusterDeployer, FileChangeListener { /*--Static Variables----------------------------------------*/ private static final Log log = LogFactory.getLog(FarmWarDeployer.class); private static final StringManager sm = StringManager.getManager(FarmWarDeployer.class); /*--Instance Variables--------------------------------------*/ protected boolean started = false; protected final HashMap<String,FileMessageFactory> fileFactories = new HashMap<>(); /** * Deployment directory. */ protected String deployDir; private File deployDirFile = null; /** * Temporary directory. */ protected String tempDir; private File tempDirFile = null; /** * Watch directory. */ protected String watchDir; private File watchDirFile = null; protected boolean watchEnabled = false; protected WarWatcher watcher = null; /** * Iteration count for background processing. */ private int count = 0; /** * Frequency of the Farm watchDir check. Cluster wide deployment will be done once for the specified amount of * backgroundProcess calls (ie, the lower the amount, the most often the checks will occur). */ protected int processDeployFrequency = 2; /** * Path where context descriptors should be deployed. */ protected File configBase = null; /** * The associated host. */ protected Host host = null; /** * MBean server. */ protected MBeanServer mBeanServer = null; /** * The associated deployer ObjectName. */ protected ObjectName oname = null; /** * The maximum valid time(in seconds) for FileMessageFactory. */ protected int maxValidTime = 5 * 60; /*--Constructor---------------------------------------------*/ public FarmWarDeployer() { } /*--Logic---------------------------------------------------*/ @Override public void start() throws Exception { if (started) { return; } Container hcontainer = getCluster().getContainer(); if (!(hcontainer instanceof Host)) { log.error(sm.getString("farmWarDeployer.hostOnly")); return; } host = (Host) hcontainer; // Check to correct engine and host setup Container econtainer = host.getParent(); if (!(econtainer instanceof Engine engine)) { log.error(sm.getString("farmWarDeployer.hostParentEngine", host.getName())); return; } String hostname = host.getName(); try { oname = new ObjectName(engine.getName() + ":type=Deployer,host=" + hostname); } catch (Exception e) { log.error(sm.getString("farmWarDeployer.mbeanNameFail", engine.getName(), hostname), e); return; } if (watchEnabled) { watcher = new WarWatcher(this, getWatchDirFile()); if (log.isInfoEnabled()) { log.info(sm.getString("farmWarDeployer.watchDir", getWatchDir())); } } configBase = host.getConfigBaseFile(); // Retrieve the MBean server mBeanServer = Registry.getRegistry(null).getMBeanServer(); started = true; count = 0; getCluster().addClusterListener(this); if (log.isInfoEnabled()) { log.info(sm.getString("farmWarDeployer.started")); } } /* * stop cluster wide deployments * * @see org.apache.catalina.ha.ClusterDeployer#stop() */ @Override public void stop() throws LifecycleException { started = false; getCluster().removeClusterListener(this); count = 0; if (watcher != null) { watcher.clear(); watcher = null; } if (log.isInfoEnabled()) { log.info(sm.getString("farmWarDeployer.stopped")); } } /** * Callback from the cluster, when a message is received, The cluster will broadcast it invoking the messageReceived * on the receiver. * * @param msg ClusterMessage - the message received from the cluster */ @Override public void messageReceived(ClusterMessage msg) { try { if (msg instanceof FileMessage fmsg) { if (log.isTraceEnabled()) { log.trace(sm.getString("farmWarDeployer.msgRxDeploy", fmsg.getContextName(), fmsg.getFileName())); } FileMessageFactory factory = getFactory(fmsg); // TODO correct second try after app is in service! if (factory.writeMessage(fmsg)) { // last message received war file is completed String name = factory.getFile().getName(); if (!name.endsWith(".war")) { name = name + ".war"; } File deployable = new File(getDeployDirFile(), name); try { String contextName = fmsg.getContextName(); if (tryAddServiced(contextName)) { try { remove(contextName); Files.move(factory.getFile().toPath(), deployable.toPath()); } catch (IOException ioe) { log.error(sm.getString("farmWarDeployer.renameFail", factory.getFile(), deployable), ioe); } finally { removeServiced(contextName); } check(contextName); if (log.isTraceEnabled()) { log.trace(sm.getString("farmWarDeployer.deployEnd", contextName)); } } else { log.error(sm.getString("farmWarDeployer.servicingDeploy", contextName, name)); } } catch (Exception e) { log.error(sm.getString("farmWarDeployer.fileMessageError"), e); } finally { removeFactory(fmsg); } } } else if (msg instanceof UndeployMessage) { try { UndeployMessage umsg = (UndeployMessage) msg; String contextName = umsg.getContextName(); if (log.isTraceEnabled()) { log.trace(sm.getString("farmWarDeployer.msgRxUndeploy", contextName)); } if (tryAddServiced(contextName)) { try { remove(contextName); } finally { removeServiced(contextName); } if (log.isTraceEnabled()) { log.trace(sm.getString("farmWarDeployer.undeployEnd", contextName)); } } else { log.error(sm.getString("farmWarDeployer.servicingUndeploy", contextName)); } } catch (Exception e) { log.error(sm.getString("farmWarDeployer.undeployMessageError"), e); } } } catch (IOException ioe) { log.error(sm.getString("farmWarDeployer.msgIoe"), ioe); } } /** * Create factory for all transported war files * * @param msg The file * * @return Factory for all app message (war files) * * @throws FileNotFoundException Missing file error * @throws IOException Other IO error */ public synchronized FileMessageFactory getFactory(FileMessage msg) throws FileNotFoundException, IOException { File writeToFile = new File(getTempDirFile(), msg.getFileName()); FileMessageFactory factory = fileFactories.get(msg.getFileName()); if (factory == null) { factory = FileMessageFactory.getInstance(writeToFile, true); factory.setMaxValidTime(maxValidTime); fileFactories.put(msg.getFileName(), factory); } return factory; } /** * Remove file (war) from messages * * @param msg The file */ public void removeFactory(FileMessage msg) { fileFactories.remove(msg.getFileName()); } /** * {@inheritDoc} * <p> * This listener accepts only FileMessage or UndeployMessage. */ @Override public boolean accept(ClusterMessage msg) { return msg instanceof FileMessage || msg instanceof UndeployMessage; } /** * Install a new web application, whose web application archive is at the specified URL, into this container and all * the other members of the cluster with the specified context name. * <p> * If this application is successfully installed locally, a ContainerEvent of type <code>INSTALL_EVENT</code> will * be sent to all registered listeners, with the newly created <code>Context</code> as an argument. * * @param contextName The context name to which this application should be installed (must be unique) * @param webapp A WAR file or unpacked directory structure containing the web application to be installed * * @exception IllegalArgumentException if the specified context name is malformed * @exception IllegalStateException if the specified context name is already deployed * @exception IOException if an input/output error was encountered during installation */ @Override public void install(String contextName, File webapp) throws IOException { Member[] members = getCluster().getMembers(); if (members.length == 0) { return; } Member localMember = getCluster().getLocalMember(); FileMessageFactory factory = FileMessageFactory.getInstance(webapp, false); FileMessage msg = new FileMessage(localMember, webapp.getName(), contextName); if (log.isTraceEnabled()) { log.trace(sm.getString("farmWarDeployer.sendStart", contextName, webapp)); } msg = factory.readMessage(msg); while (msg != null) { for (Member member : members) { if (log.isTraceEnabled()) { log.trace(sm.getString("farmWarDeployer.sendFragment", contextName, webapp, member)); } getCluster().send(msg, member); } msg = factory.readMessage(msg); } if (log.isTraceEnabled()) { log.trace(sm.getString("farmWarDeployer.sendEnd", contextName, webapp)); } } /** * Remove an existing web application, attached to the specified context name. If this application is successfully * removed, a ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all registered listeners, with the * removed <code>Context</code> as an argument. Deletes the web application war file and/or directory if they exist * in the Host's appBase. * * @param contextName The context name of the application to be removed * @param undeploy boolean flag to remove web application from server * * @exception IllegalArgumentException if the specified context name is malformed * @exception IllegalArgumentException if the specified context name does not identify a currently installed web * application * @exception IOException if an input/output error occurs during removal */ @Override public void remove(String contextName, boolean undeploy) throws IOException { if (getCluster().getMembers().length > 0) { if (log.isInfoEnabled()) { log.info(sm.getString("farmWarDeployer.removeStart", contextName)); } Member localMember = getCluster().getLocalMember(); UndeployMessage msg = new UndeployMessage(localMember, System.currentTimeMillis(), "Undeploy:" + contextName + ":" + System.currentTimeMillis(), contextName); if (log.isTraceEnabled()) { log.trace(sm.getString("farmWarDeployer.removeTxMsg", contextName)); } cluster.send(msg); } // remove locally if (undeploy) { try { if (tryAddServiced(contextName)) { try { remove(contextName); } finally { removeServiced(contextName); } check(contextName); } else { log.error(sm.getString("farmWarDeployer.removeFailRemote", contextName)); } } catch (Exception e) { log.error(sm.getString("farmWarDeployer.removeFailLocal", contextName), e); } } } @Override public void fileModified(File newWar) { try { File deployWar = new File(getDeployDirFile(), newWar.getName()); ContextName cn = new ContextName(deployWar.getName(), true); if (deployWar.exists() && deployWar.lastModified() > newWar.lastModified()) { if (log.isInfoEnabled()) { log.info(sm.getString("farmWarDeployer.alreadyDeployed", cn.getName())); } return; } if (log.isInfoEnabled()) { log.info(sm.getString("farmWarDeployer.modInstall", cn.getName(), deployWar.getAbsolutePath())); } // install local if (tryAddServiced(cn.getName())) { try { copy(newWar, deployWar); } finally { removeServiced(cn.getName()); } check(cn.getName()); } else { log.error(sm.getString("farmWarDeployer.servicingDeploy", cn.getName(), deployWar.getName())); } install(cn.getName(), deployWar); } catch (Exception e) { log.error(sm.getString("farmWarDeployer.modInstallFail"), e); } } @Override public void fileRemoved(File removeWar) { try { ContextName cn = new ContextName(removeWar.getName(), true); if (log.isInfoEnabled()) { log.info(sm.getString("farmWarDeployer.removeLocal", cn.getName())); } remove(cn.getName(), true); } catch (Exception e) { log.error(sm.getString("farmWarDeployer.removeLocalFail"), e); } } /** * Invoke the remove method on the deployer. * * @param contextName The context to remove * * @throws Exception If an error occurs removing the context */ protected void remove(String contextName) throws Exception { // TODO Handle remove also work dir content ! // Stop the context first to be nicer Context context = (Context) host.findChild(contextName); if (context != null) { if (log.isDebugEnabled()) { log.debug(sm.getString("farmWarDeployer.undeployLocal", contextName)); } context.stop(); String baseName = context.getBaseName(); File war = new File(host.getAppBaseFile(), baseName + ".war"); File dir = new File(host.getAppBaseFile(), baseName); File xml = new File(configBase, baseName + ".xml"); if (war.exists()) { if (!war.delete()) { log.error(sm.getString("farmWarDeployer.deleteFail", war)); } } else if (dir.exists()) { undeployDir(dir); } else { if (!xml.delete()) { log.error(sm.getString("farmWarDeployer.deleteFail", xml)); } } } } /** * Delete the specified directory, including all of its contents and subdirectories recursively. * * @param dir File object representing the directory to be deleted */ protected void undeployDir(File dir) { String[] files = dir.list(); if (files == null) { files = new String[0]; } for (String s : files) { File file = new File(dir, s); if (file.isDirectory()) { undeployDir(file); } else { if (!file.delete()) { log.error(sm.getString("farmWarDeployer.deleteFail", file)); } } } if (!dir.delete()) { log.error(sm.getString("farmWarDeployer.deleteFail", dir)); } } /** * Call watcher to check for deploy changes * * @see org.apache.catalina.ha.ClusterDeployer#backgroundProcess() */ @Override public void backgroundProcess() { if (started) { if (watchEnabled) { count = (count + 1) % processDeployFrequency; if (count == 0) { watcher.check(); } } removeInvalidFileFactories(); } } /*--Deployer Operations ------------------------------------*/ /** * Check a context for deployment operations. * * @param name The context name * * @throws Exception Error invoking the deployer */ protected void check(String name) throws Exception { String[] params = { name }; String[] signature = { "java.lang.String" }; mBeanServer.invoke(oname, "check", params, signature); } /** * Attempt to mark a context as being serviced * * @param name The context name * * @return {@code true} if the application was marked as being serviced and {@code false} if the application was * already marked as being serviced * * @throws Exception Error invoking the deployer */ protected boolean tryAddServiced(String name) throws Exception { String[] params = { name }; String[] signature = { "java.lang.String" }; Boolean result = (Boolean) mBeanServer.invoke(oname, "tryAddServiced", params, signature); return result.booleanValue(); } /** * Mark a context as no longer being serviced. * * @param name The context name * * @throws Exception Error invoking the deployer */ protected void removeServiced(String name) throws Exception { String[] params = { name }; String[] signature = { "java.lang.String" }; mBeanServer.invoke(oname, "removeServiced", params, signature); } /*--Instance Getters/Setters--------------------------------*/ public String getDeployDir() { return deployDir; } public File getDeployDirFile() { if (deployDirFile != null) { return deployDirFile; } File dir = getAbsolutePath(getDeployDir()); this.deployDirFile = dir; return dir; } public void setDeployDir(String deployDir) { this.deployDir = deployDir; } public String getTempDir() { return tempDir; } public File getTempDirFile() { if (tempDirFile != null) { return tempDirFile; } File dir = getAbsolutePath(getTempDir()); this.tempDirFile = dir; return dir; } public void setTempDir(String tempDir) { this.tempDir = tempDir; } public String getWatchDir() { return watchDir; } public File getWatchDirFile() { if (watchDirFile != null) { return watchDirFile; } File dir = getAbsolutePath(getWatchDir()); this.watchDirFile = dir; return dir; } public void setWatchDir(String watchDir) { this.watchDir = watchDir; } public boolean isWatchEnabled() { return watchEnabled; } public boolean getWatchEnabled() { return watchEnabled; } public void setWatchEnabled(boolean watchEnabled) { this.watchEnabled = watchEnabled; } /** * @return the frequency of watcher checks. */ public int getProcessDeployFrequency() { return this.processDeployFrequency; } /** * Set the watcher checks frequency. * * @param processExpiresFrequency the new manager checks frequency */ public void setProcessDeployFrequency(int processExpiresFrequency) { if (processExpiresFrequency <= 0) { return; } this.processDeployFrequency = processExpiresFrequency; } public int getMaxValidTime() { return maxValidTime; } public void setMaxValidTime(int maxValidTime) { this.maxValidTime = maxValidTime; } /** * Copy a file to the specified temp directory. * * @param from copy from temp * @param to to host appBase directory * * @return true, copy successful */ protected boolean copy(File from, File to) { try { if (!to.exists()) { if (!to.createNewFile()) { log.error(sm.getString("fileNewFail", to)); return false; } } } catch (IOException ioe) { log.error(sm.getString("farmWarDeployer.fileCopyFail", from, to), ioe); return false; } try (java.io.FileInputStream is = new java.io.FileInputStream(from); java.io.FileOutputStream os = new java.io.FileOutputStream(to, false)) { byte[] buf = new byte[4096]; while (true) { int len = is.read(buf); if (len < 0) { break; } os.write(buf, 0, len); } } catch (IOException ioe) { log.error(sm.getString("farmWarDeployer.fileCopyFail", from, to), ioe); return false; } return true; } protected void removeInvalidFileFactories() { String[] fileNames = fileFactories.keySet().toArray(new String[0]); for (String fileName : fileNames) { FileMessageFactory factory = fileFactories.get(fileName); if (!factory.isValid()) { fileFactories.remove(fileName); } } } private File getAbsolutePath(String path) { File dir = new File(path); if (!dir.isAbsolute()) { dir = new File(getCluster().getContainer().getCatalinaBase(), dir.getPath()); } try { dir = dir.getCanonicalFile(); } catch (IOException ignore) { // Ignore } return dir; } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
5.33
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9