This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,787 @@
/*
* 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.IOException;
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.
*
* @author Peter Rossbach
*/
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)) {
log.error(sm.getString("farmWarDeployer.hostParentEngine",
host.getName()));
return ;
}
Engine engine = (Engine) econtainer;
String hostname = null;
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, 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) {
FileMessage fmsg = (FileMessage) msg;
if (log.isDebugEnabled())
log.debug(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 (!isServiced(contextName)) {
addServiced(contextName);
try {
remove(contextName);
if (!factory.getFile().renameTo(deployable)) {
log.error(sm.getString(
"farmWarDeployer.renameFail",
factory.getFile(), deployable));
}
check(contextName);
} finally {
removeServiced(contextName);
}
if (log.isDebugEnabled())
log.debug(sm.getString(
"farmWarDeployer.deployEnd",
contextName));
} else
log.error(sm.getString(
"farmWarDeployer.servicingDeploy",
contextName, name));
} catch (Exception ex) {
log.error(ex);
} finally {
removeFactory(fmsg);
}
}
} else if (msg instanceof UndeployMessage) {
try {
UndeployMessage umsg = (UndeployMessage) msg;
String contextName = umsg.getContextName();
if (log.isDebugEnabled())
log.debug(sm.getString("farmWarDeployer.msgRxUndeploy",
contextName));
if (!isServiced(contextName)) {
addServiced(contextName);
try {
remove(contextName);
} finally {
removeServiced(contextName);
}
if (log.isDebugEnabled())
log.debug(sm.getString(
"farmWarDeployer.undeployEnd",
contextName));
} else
log.error(sm.getString(
"farmWarDeployer.servicingUndeploy",
contextName));
} catch (Exception ex) {
log.error(ex);
}
}
} catch (java.io.IOException x) {
log.error(sm.getString("farmWarDeployer.msgIoe"), x);
}
}
/**
* Create factory for all transported war files
*
* @param msg The file
* @return Factory for all app message (war files)
* @throws java.io.FileNotFoundException Missing file error
* @throws java.io.IOException Other IO error
*/
public synchronized FileMessageFactory getFactory(FileMessage msg)
throws java.io.FileNotFoundException, java.io.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());
}
/**
* Before the cluster invokes messageReceived the cluster will ask the
* receiver to accept or decline the message, In the future, when messages
* get big, the accept method will only take a message header
*
* @param msg ClusterMessage
* @return boolean - returns true to indicate that messageReceived should be
* invoked. If false is returned, the messageReceived method will
* not be invoked.
*/
@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.isDebugEnabled())
log.debug(sm.getString("farmWarDeployer.sendStart", contextName,
webapp));
msg = factory.readMessage(msg);
while (msg != null) {
for (int i = 0; i < members.length; i++) {
if (log.isDebugEnabled())
log.debug(sm.getString("farmWarDeployer.sendFragment",
contextName, webapp, members[i]));
getCluster().send(msg, members[i]);
}
msg = factory.readMessage(msg);
}
if(log.isDebugEnabled())
log.debug(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.isDebugEnabled())
log.debug(sm.getString("farmWarDeployer.removeTxMsg", contextName));
cluster.send(msg);
}
// remove locally
if (undeploy) {
try {
if (!isServiced(contextName)) {
addServiced(contextName);
try {
remove(contextName);
} finally {
removeServiced(contextName);
}
} else
log.error(sm.getString("farmWarDeployer.removeFailRemote",
contextName));
} catch (Exception ex) {
log.error(sm.getString("farmWarDeployer.removeFailLocal",
contextName), ex);
}
}
}
/**
* Modification from watchDir war detected!
*
* @see org.apache.catalina.ha.deploy.FileChangeListener#fileModified(File)
*/
@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 (!isServiced(cn.getName())) {
addServiced(cn.getName());
try {
copy(newWar, deployWar);
check(cn.getName());
} finally {
removeServiced(cn.getName());
}
} else {
log.error(sm.getString("farmWarDeployer.servicingDeploy",
cn.getName(), deployWar.getName()));
}
install(cn.getName(), deployWar);
} catch (Exception x) {
log.error(sm.getString("farmWarDeployer.modInstallFail"), x);
}
}
/**
* War remove from watchDir
*
* @see org.apache.catalina.ha.deploy.FileChangeListener#fileRemoved(File)
*/
@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 x) {
log.error(sm.getString("farmWarDeployer.removeLocalFail"), x);
}
}
/**
* 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));
}
}
// Perform new deployment and remove internal HostConfig state
check(contextName);
}
}
/**
* 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 (int i = 0; i < files.length; i++) {
File file = new File(dir, files[i]);
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);
}
/**
* Verified if a context is being services.
* @param name The context name
* @return <code>true</code> if the context is being serviced
* @throws Exception Error invoking the deployer
*/
protected boolean isServiced(String name) throws Exception {
String[] params = { name };
String[] signature = { "java.lang.String" };
Boolean result = (Boolean) mBeanServer.invoke(oname, "isServiced",
params, signature);
return result.booleanValue();
}
/**
* Mark a context as being services.
* @param name The context name
* @throws Exception Error invoking the deployer
*/
protected void addServiced(String name) throws Exception {
String[] params = { name };
String[] signature = { "java.lang.String" };
mBeanServer.invoke(oname, "addServiced", params, signature);
}
/**
* 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--------------------------------*/
@Override
public boolean equals(Object listener) {
return super.equals(listener);
}
@Override
public int hashCode() {
return super.hashCode();
}
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 e) {
log.error(sm.getString("farmWarDeployer.fileCopyFail",
from, to), e);
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 e) {
log.error(sm.getString("farmWarDeployer.fileCopyFail",
from, to), e);
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 e) {// ignore
}
return dir;
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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;
public interface FileChangeListener {
public void fileModified(File f);
public void fileRemoved(File f);
}

View File

@@ -0,0 +1,88 @@
/*
* 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 org.apache.catalina.ha.ClusterMessageBase;
import org.apache.catalina.tribes.Member;
/**
* Contains the data for a file being transferred over TCP, this is
* essentially a fragment of a file, read and written by the FileMessageFactory
* @version 1.0
*/
public class FileMessage extends ClusterMessageBase {
private static final long serialVersionUID = 2L;
private int messageNumber;
private byte[] data;
private int dataLength;
private long totalNrOfMsgs;
private final String fileName;
private final String contextName;
public FileMessage(Member source,
String fileName,
String contextName) {
this.address=source;
this.fileName=fileName;
this.contextName=contextName;
}
public int getMessageNumber() {
return messageNumber;
}
public void setMessageNumber(int messageNumber) {
this.messageNumber = messageNumber;
}
public long getTotalNrOfMsgs() {
return totalNrOfMsgs;
}
public void setTotalNrOfMsgs(long totalNrOfMsgs) {
this.totalNrOfMsgs = totalNrOfMsgs;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data, int length) {
this.data = data;
this.dataLength = length;
}
public int getDataLength() {
return dataLength;
}
@Override
public String getUniqueId() {
StringBuilder result = new StringBuilder(getFileName());
result.append("#-#");
result.append(getMessageNumber());
result.append("#-#");
result.append(System.currentTimeMillis());
return result.toString();
}
public String getFileName() {
return fileName;
}
public String getContextName() {
return contextName;
}
}

View File

@@ -0,0 +1,402 @@
/*
* 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.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.HexUtils;
import org.apache.tomcat.util.res.StringManager;
/**
* This factory is used to read files and write files by splitting them up into
* smaller messages. So that entire files don't have to be read into memory.
* <BR>
* The factory can be used as a reader or writer but not both at the same time.
* When done reading or writing the factory will close the input or output
* streams and mark the factory as closed. It is not possible to use it after
* that. <BR>
* To force a cleanup, call cleanup() from the calling object. <BR>
* This class is not thread safe.
*
* @version 1.0
*/
public class FileMessageFactory {
/*--Static Variables----------------------------------------*/
private static final Log log = LogFactory.getLog(FileMessageFactory.class);
private static final StringManager sm = StringManager.getManager(FileMessageFactory.class);
/**
* The number of bytes that we read from file
*/
public static final int READ_SIZE = 1024 * 10; //10kb
/**
* The file that we are reading/writing
*/
protected final File file;
/**
* True means that we are writing with this factory. False means that we are
* reading with this factory
*/
protected final boolean openForWrite;
/**
* Once the factory is used, it cannot be reused.
*/
protected boolean closed = false;
/**
* When openForWrite=false, the input stream is held by this variable
*/
protected FileInputStream in;
/**
* When openForWrite=true, the output stream is held by this variable
*/
protected FileOutputStream out;
/**
* The number of messages we have written
*/
protected int nrOfMessagesProcessed = 0;
/**
* The total size of the file
*/
protected long size = 0;
/**
* The total number of packets that we split this file into
*/
protected long totalNrOfMessages = 0;
/**
* The number of the last message processed. Message IDs are 1 based.
*/
protected AtomicLong lastMessageProcessed = new AtomicLong(0);
/**
* Messages received out of order are held in the buffer until required. If
* everything is worked as expected, messages will spend very little time in
* the buffer.
*/
protected final Map<Long, FileMessage> msgBuffer = new ConcurrentHashMap<>();
/**
* The bytes that we hold the data in, not thread safe.
*/
protected byte[] data = new byte[READ_SIZE];
/**
* Flag that indicates if a thread is writing messages to disk. Access to
* this flag must be synchronised.
*/
protected boolean isWriting = false;
/**
* The time this instance was created. (in milliseconds)
*/
protected long creationTime = 0;
/**
* The maximum valid time(in seconds) from creationTime.
*/
protected int maxValidTime = -1;
/**
* Private constructor, either instantiates a factory to read or write. <BR>
* When openForWrite==true, then a the file, f, will be created and an
* output stream is opened to write to it. <BR>
* When openForWrite==false, an input stream is opened, the file has to
* exist.
*
* @param f
* File - the file to be read/written
* @param openForWrite
* boolean - true means we are writing to the file, false means
* we are reading from the file
* @throws FileNotFoundException -
* if the file to be read doesn't exist
* @throws IOException -
* if the system fails to open input/output streams to the file
* or if it fails to create the file to be written to.
*/
private FileMessageFactory(File f, boolean openForWrite)
throws FileNotFoundException, IOException {
this.file = f;
this.openForWrite = openForWrite;
if (log.isDebugEnabled())
log.debug("open file " + f + " write " + openForWrite);
if (openForWrite) {
if (!file.exists())
if (!file.createNewFile()) {
throw new IOException(sm.getString("fileNewFail", file));
}
out = new FileOutputStream(f);
} else {
size = file.length();
totalNrOfMessages = (size / READ_SIZE) + 1;
in = new FileInputStream(f);
}//end if
creationTime = System.currentTimeMillis();
}
/**
* Creates a factory to read or write from a file. When opening for read,
* the readMessage can be invoked, and when opening for write the
* writeMessage can be invoked.
*
* @param f
* File - the file to be read or written
* @param openForWrite
* boolean - true, means we are writing to the file, false means
* we are reading from it
* @throws FileNotFoundException -
* if the file to be read doesn't exist
* @throws IOException -
* if it fails to create the file that is to be written
* @return FileMessageFactory
*/
public static FileMessageFactory getInstance(File f, boolean openForWrite)
throws FileNotFoundException, IOException {
return new FileMessageFactory(f, openForWrite);
}
/**
* Reads file data into the file message and sets the size, totalLength,
* totalNrOfMsgs and the message number <BR>
* If EOF is reached, the factory returns null, and closes itself, otherwise
* the same message is returned as was passed in. This makes sure that not
* more memory is ever used. To remember, neither the file message or the
* factory are thread safe. dont hand off the message to one thread and read
* the same with another.
*
* @param f
* FileMessage - the message to be populated with file data
* @throws IllegalArgumentException -
* if the factory is for writing or is closed
* @throws IOException -
* if a file read exception occurs
* @return FileMessage - returns the same message passed in as a parameter,
* or null if EOF
*/
public FileMessage readMessage(FileMessage f)
throws IllegalArgumentException, IOException {
checkState(false);
int length = in.read(data);
if (length == -1) {
cleanup();
return null;
} else {
f.setData(data, length);
f.setTotalNrOfMsgs(totalNrOfMessages);
f.setMessageNumber(++nrOfMessagesProcessed);
return f;
}//end if
}
/**
* Writes a message to file. If (msg.getMessageNumber() ==
* msg.getTotalNrOfMsgs()) the output stream will be closed after writing.
*
* @param msg
* FileMessage - message containing data to be written
* @throws IllegalArgumentException -
* if the factory is opened for read or closed
* @throws IOException -
* if a file write error occurs
* @return returns true if the file is complete and outputstream is closed,
* false otherwise.
*/
public boolean writeMessage(FileMessage msg)
throws IllegalArgumentException, IOException {
if (!openForWrite) {
throw new IllegalArgumentException(sm.getString("fileMessageFactory.cannotWrite"));
}
if (log.isDebugEnabled())
log.debug("Message " + msg + " data " + HexUtils.toHexString(msg.getData())
+ " data length " + msg.getDataLength() + " out " + out);
if (msg.getMessageNumber() <= lastMessageProcessed.get()) {
// Duplicate of message already processed
log.warn(sm.getString("fileMessageFactory.duplicateMessage", msg.getContextName(), msg.getFileName(),
HexUtils.toHexString(msg.getData()), Integer.valueOf(msg.getDataLength())));
return false;
}
FileMessage previous =
msgBuffer.put(Long.valueOf(msg.getMessageNumber()), msg);
if (previous != null) {
// Duplicate of message not yet processed
log.warn(sm.getString("fileMessageFactory.duplicateMessage", msg.getContextName(), msg.getFileName(),
HexUtils.toHexString(msg.getData()), Integer.valueOf(msg.getDataLength())));
return false;
}
FileMessage next = null;
synchronized (this) {
if (!isWriting) {
next = msgBuffer.get(Long.valueOf(lastMessageProcessed.get() + 1));
if (next != null) {
isWriting = true;
} else {
return false;
}
} else {
return false;
}
}
while (next != null) {
out.write(next.getData(), 0, next.getDataLength());
lastMessageProcessed.incrementAndGet();
out.flush();
if (next.getMessageNumber() == next.getTotalNrOfMsgs()) {
out.close();
cleanup();
return true;
}
synchronized(this) {
next =
msgBuffer.get(Long.valueOf(lastMessageProcessed.get() + 1));
if (next == null) {
isWriting = false;
}
}
}
return false;
}//writeMessage
/**
* Closes the factory, its streams and sets all its references to null
*/
public void cleanup() {
if (in != null)
try {
in.close();
} catch (IOException ignore) {
}
if (out != null)
try {
out.close();
} catch (IOException ignore) {
}
in = null;
out = null;
size = 0;
closed = true;
data = null;
nrOfMessagesProcessed = 0;
totalNrOfMessages = 0;
msgBuffer.clear();
lastMessageProcessed = null;
}
/**
* Check to make sure the factory is able to perform the function it is
* asked to do. Invoked by readMessage/writeMessage before those methods
* proceed.
*
* @param openForWrite The value to check
* @throws IllegalArgumentException if the state is not the expected one
*/
protected void checkState(boolean openForWrite)
throws IllegalArgumentException {
if (this.openForWrite != openForWrite) {
cleanup();
if (openForWrite) {
throw new IllegalArgumentException(sm.getString("fileMessageFactory.cannotWrite"));
} else {
throw new IllegalArgumentException(sm.getString("fileMessageFactory.cannotRead"));
}
}
if (this.closed) {
cleanup();
throw new IllegalArgumentException(sm.getString("fileMessageFactory.closed"));
}
}
/**
* Example usage.
*
* @param args
* String[], args[0] - read from filename, args[1] write to
* filename
* @throws Exception An error occurred
*/
public static void main(String[] args) throws Exception {
System.out.println("Usage: FileMessageFactory fileToBeRead fileToBeWritten");
System.out.println("Usage: This will make a copy of the file on the local file system");
FileMessageFactory read = getInstance(new File(args[0]), false);
FileMessageFactory write = getInstance(new File(args[1]), true);
FileMessage msg = new FileMessage(null, args[0], args[0]);
msg = read.readMessage(msg);
if (msg == null) {
System.out.println("Empty input file : " + args[0]);
return;
}
System.out.println("Expecting to write " + msg.getTotalNrOfMsgs()
+ " messages.");
int cnt = 0;
while (msg != null) {
write.writeMessage(msg);
cnt++;
msg = read.readMessage(msg);
}//while
System.out.println("Actually wrote " + cnt + " messages.");
}///main
public File getFile() {
return file;
}
public boolean isValid() {
if (maxValidTime > 0) {
long timeNow = System.currentTimeMillis();
int timeIdle = (int) ((timeNow - creationTime) / 1000L);
if (timeIdle > maxValidTime) {
cleanup();
if (file.exists() && !file.delete()) {
log.warn(sm.getString("fileMessageFactory.deleteFail", file));
}
return false;
}
}
return true;
}
public int getMaxValidTime() {
return maxValidTime;
}
public void setMaxValidTime(int maxValidTime) {
this.maxValidTime = maxValidTime;
}
}

View File

@@ -0,0 +1,59 @@
# 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.
farmWarDeployer.alreadyDeployed=webapp [{0}] are already deployed.
farmWarDeployer.deleteFail=Failed to delete [{0}]
farmWarDeployer.deployEnd=Deployment from [{0}] finished.
farmWarDeployer.fileCopyFail=Unable to copy from [{0}] to [{1}]
farmWarDeployer.hostOnly=FarmWarDeployer can only work as host cluster subelement!
farmWarDeployer.hostParentEngine=FarmWarDeployer can only work if parent of [{0}] is an engine!
farmWarDeployer.mbeanNameFail=Cannot construct MBean object name for engine [{0}] and host [{1}]
farmWarDeployer.modInstall=Installing webapp [{0}] from [{1}]
farmWarDeployer.modInstallFail=Unable to install WAR file
farmWarDeployer.modRemoveFail=No removal
farmWarDeployer.msgIoe=Unable to read farm deploy file message.
farmWarDeployer.msgRxDeploy=Receive cluster deployment path [{0}], war [{1}]
farmWarDeployer.msgRxUndeploy=Receive cluster undeployment from path [{0}]
farmWarDeployer.removeFailLocal=Local remove from [{0}] failed
farmWarDeployer.removeFailRemote=Local remove from [{0}] failed, other manager has app in service!
farmWarDeployer.removeLocal=Removing webapp [{0}]
farmWarDeployer.removeLocalFail=Unable to remove WAR file
farmWarDeployer.removeStart=Cluster wide remove of web app [{0}]
farmWarDeployer.removeTxMsg=Send cluster wide undeployment from [{0}]
farmWarDeployer.renameFail=Failed to rename [{0}] to [{1}]
farmWarDeployer.sendEnd=Send cluster war deployment path [{0}], war [{1}] finished.
farmWarDeployer.sendFragment=Send cluster war fragment path [{0}], war [{1}] to [{2}]
farmWarDeployer.sendStart=Send cluster war deployment path [{0}], war [{1}] started.
farmWarDeployer.servicingDeploy=Application [{0}] is being serviced. Touch war file [{1}] again!
farmWarDeployer.servicingUndeploy=Application [{0}] is being serviced and can''t be removed from backup cluster node
farmWarDeployer.started=Cluster FarmWarDeployer started.
farmWarDeployer.stopped=Cluster FarmWarDeployer stopped.
farmWarDeployer.undeployEnd=Undeployment from [{0}] finished.
farmWarDeployer.undeployLocal=Undeploy local context [{0}]
farmWarDeployer.watchDir=Cluster deployment is watching [{0}] for changes.
fileMessageFactory.cannotRead=Cannot read message, this factory is writing
fileMessageFactory.cannotWrite=Cannot write message, this factory is reading
fileMessageFactory.closed=Factory has been closed
fileMessageFactory.deleteFail=Failed to delete [{0}]
fileMessageFactory.duplicateMessage=Received duplicate message. Is the Sender timeout too low? context: [{0}] filename: [{1}] data: [{2}] data length: [{3}]
fileNewFail=Unable to create [{0}]
warWatcher.cantListWatchDir=Cannot list files in WatchDir [{0}]: check to see if it is a directory and has read permissions.
warWatcher.checkWarResult=WarInfo.check() returned [{0}] for [{1}]
warWatcher.checkingWar=Checking WAR file [{0}]
warWatcher.checkingWars=Checking WARs in [{0}]
warWatcher.listedFileDoesNotExist=[{0}] was detected in [{1}] but does not exist. Check directory permissions on [{1}]?

View File

@@ -0,0 +1,25 @@
# 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.
farmWarDeployer.modInstall=Installiere Webapplikation [{0}] von [{1}]
farmWarDeployer.modInstallFail=WAR-Datei konnte nicht installiert werden
farmWarDeployer.msgIoe=Die Farm-Deploy-Datei-Nachricht kann nicht gelesen werden.
farmWarDeployer.servicingUndeploy=Applikation [{0}] ist noch aktiv und kann nicht vom Backup-Cluster-Knoten entfernt werden
farmWarDeployer.undeployEnd=Undeployment von [{0}] beendet.
fileMessageFactory.duplicateMessage=Doppelte Nachricht empfangen. Ist der Timeout für den Sender zu niedrig? Context: [{0}] Dateiname: [{1}] Daten: [{2}] Datenlänge [{3}]
warWatcher.cantListWatchDir=Dateien in WatchDir [{0}] können nicht gelistet werdenm: Prüfen Sie dass Lesezugriff auf das Verzeichnis besteht
warWatcher.checkingWar=WAR-Datei [{0}] wird geprüft.

View File

@@ -0,0 +1,31 @@
# 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.
farmWarDeployer.hostOnly=FarmWarDeployer sólo puede operar como un subelemento de una máquina del cluster!
farmWarDeployer.modInstall=Installando webapp [{0}] desde [{1}]\n
farmWarDeployer.modInstallFail=Incapaz de instalar WAR file
farmWarDeployer.msgIoe=Incapáz de leer el archivo de despliegue de la granja
farmWarDeployer.msgRxDeploy=Recibe el camino de despliegue del cluster [{0}], war [{1}]
farmWarDeployer.removeFailLocal=Borrado local de [{0}] fallido
farmWarDeployer.removeFailRemote=El borrado local de [{0}] falló, otro manager tiene la aplicación en servicio!
farmWarDeployer.sendFragment=Fragmento war enviado al cluster con camino [{0}], war [{1}] a [{2}]\n
farmWarDeployer.servicingUndeploy=La applicación [{0}] esta en servicion y no pude ser removida del nodo de respaldo del cluster
farmWarDeployer.undeployEnd=El revertimiendo del despliegue de [{0}] ha terminado.\n
fileMessageFactory.deleteFail=Fallo al borrar [{0}]\n
warWatcher.cantListWatchDir=No se pueden listar archivos en WatchDir [{0}]: verifique si es un directorio y tiene permisos de lectura.\n
warWatcher.checkWarResult=WarInfo.check() devolvió [{0}] para [{1}]
warWatcher.checkingWar=Verificando archivo WAR [{0}]

View File

@@ -0,0 +1,58 @@
# 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.
farmWarDeployer.alreadyDeployed=l''application web [{0}] est déjà déployée
farmWarDeployer.deleteFail=Pas réussi à supprimer [{0}]
farmWarDeployer.deployEnd=Le déploiement de [{0}] est terminé
farmWarDeployer.fileCopyFail=Impossible de copier depuis [{0}] vers [{1}]
farmWarDeployer.hostOnly=Le FarmWarDeployer ne fonctionne qu'en tant que sous-élément d'un "host cluster" !
farmWarDeployer.hostParentEngine=FarmWarDeployer peut fonctionner uniquement si le parent de [{0}] est un moteur
farmWarDeployer.mbeanNameFail=Impossible de construire le nom d''objet du mbean pour le moteur [{0}] et l''hôte [{1}]
farmWarDeployer.modInstall=Installation en cours pour la webapp [{0}] depuis [{1}]
farmWarDeployer.modInstallFail=Incapable d'installer le fichier WAR
farmWarDeployer.msgIoe=Incapable de lire le message de déploiement dans la ferme
farmWarDeployer.msgRxDeploy=Recu le chemin de déploiement [{0}] du cluster, war [{1}]
farmWarDeployer.msgRxUndeploy=Réception d''un retrait de cluster ("cluster undeployment") du chemin [{0}]
farmWarDeployer.removeFailLocal=Impossible d''enlever localement de [{0}]
farmWarDeployer.removeFailRemote=La suppression locale de [{0}] a échouée, l''autre gestionnaire (manager) a l''app en fonction !
farmWarDeployer.removeLocal=Retrait de l''application web [{0}]
farmWarDeployer.removeLocalFail=Impossible d'enlever le fichier WAR
farmWarDeployer.removeStart=Retrait de l''application web [{0}] dans tout le cluster
farmWarDeployer.removeTxMsg=Envoi à tout le cluster du déploiement à partir de [{0}]
farmWarDeployer.renameFail=Echec du renommage de [{0}] en [{1}]
farmWarDeployer.sendEnd=Envoi du chemin de déploiement du war au cluster, war [{1}] terminé
farmWarDeployer.sendFragment=Envoi du chemin du fragment du war du cluster [{0}], war [{1}] vers [{2}]
farmWarDeployer.sendStart=Envoi du déploiement war en cluster chemin [{0}], war [{1}] démarré
farmWarDeployer.servicingDeploy=L''application [{0}] est en cours de maintenance, mettez de nouveau à jour la date du fichier war [{1}]
farmWarDeployer.servicingUndeploy=L''application [{0}] est en maintenance et ne peut être retirée du node backup du cluster
farmWarDeployer.started=Le FarmWarDeployer du cluster a démarré
farmWarDeployer.stopped=Le FarmWarDeployer du cluster a été arrêté
farmWarDeployer.undeployEnd=Retrait de [{0}] terminé
farmWarDeployer.undeployLocal=Le contexte local [{0}] est retiré
farmWarDeployer.watchDir=Le déploiement du cluster surveille [{0}] pour des modifications
fileMessageFactory.cannotRead=Impossible de lire un message, cette fabrique est en train d'écrire
fileMessageFactory.cannotWrite=Impossible d'écrire un message, cette fabrique est en train de lire
fileMessageFactory.closed=La fabrique a été fermée
fileMessageFactory.deleteFail=Impossible de supprimer [{0}]
fileMessageFactory.duplicateMessage=Réception de message en double, le délai d''attente maximum de l''expéditeur pourrait être trop court; contexte: [{0}] nom de fichier: [{1}] données: [{2}] longueur des données: [{3}]
fileNewFail=Impossible de créer [{0}]
warWatcher.cantListWatchDir=Incapacité de lister les fichiers dans le répertoire WatchDir [{0}]: vérifiez qu''il s''agit d''un répertoire et qu''il a la permission de lecture.
warWatcher.checkWarResult=WarInfo.check() a retourné [{0}] pour [{1}]
warWatcher.checkingWar=Vérification du fichier WAR [{0}]
warWatcher.checkingWars=Vérification des WARs dans [{0}]
warWatcher.listedFileDoesNotExist=[{0}] a été détecté dans [{1}] mais n''existe pas, les permissions du répertoire pourraient être incorrectes

View File

@@ -0,0 +1,58 @@
# 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.
farmWarDeployer.alreadyDeployed=webapp [{0}]は既にデプロイされています。
farmWarDeployer.deleteFail=[{0}] を削除できません。
farmWarDeployer.deployEnd=[{0}]からの配備が完了しました。
farmWarDeployer.fileCopyFail=[{0}] から [{1}] へコピーできません。
farmWarDeployer.hostOnly=FarmWarDeployer はHostクラスタのサブエレメントとしてのみ機能します
farmWarDeployer.hostParentEngine=FarmWarDeployer は親 [{0}] が Engine のインスタンスでなければ機能しません。
farmWarDeployer.mbeanNameFail=エンジン[{0}]とホスト[{1}]のMBeanオブジェクト名を構築できません
farmWarDeployer.modInstall=[{1}]からwebapp [{0}]をインストールしています。
farmWarDeployer.modInstallFail=WAR ファイルをインストールできませんでした。
farmWarDeployer.msgIoe=ファームデプロイファイルメッセージの読み取りに失敗しました。
farmWarDeployer.msgRxDeploy=クラスタ展開パス[{0}]、WAR[{1}]を受信しました。
farmWarDeployer.msgRxUndeploy=パス[{0}]からクラスタの展開解除を受信しました。
farmWarDeployer.removeFailLocal=[{0}]からのローカル削除に失敗しました
farmWarDeployer.removeFailRemote=ローカルの [{0}] を削除できませんでした。他のマネージャーノードでサービス中です !
farmWarDeployer.removeLocal=webapp [{0}]を削除しています
farmWarDeployer.removeLocalFail=WAR ファイルを削除できません。
farmWarDeployer.removeStart=Webアプリケーション[{0}]のクラスタ削除。
farmWarDeployer.removeTxMsg=コンテキスト [{0}] からクラスターに配置解除メッセージを送信しました。
farmWarDeployer.renameFail=[{0}] から [{1}] へ名前を変更できません。
farmWarDeployer.sendEnd=クラスタWarデプロイメント パス[{0}]を送信しました。WAR[{1}]を完了しました。
farmWarDeployer.sendFragment=コンテキスト [{0}] の war [{1}] をクラスターメンバー [{2}] へ送信します。
farmWarDeployer.sendStart=クラスタWar展開パス[{0}]を送信し、War[{1}]を開始しました。
farmWarDeployer.servicingDeploy=アプリケーション [{0}] はすでにサービスを開始しています。もう一度 WAR ファイル [{1}] を更新してください。
farmWarDeployer.servicingUndeploy=アプリケーション [{0}] はサービス中のためバックアップクラスタノードから削除できません。
farmWarDeployer.started=クラスターの FarmWarDeployer を開始しました。
farmWarDeployer.stopped=Cluster FarmWarDeployer が停止しました。
farmWarDeployer.undeployEnd=コンテキスト [{0}] の配置解除が完了しました。
farmWarDeployer.undeployLocal=ローカルコンテキスト [{0}] を配置解除します。
farmWarDeployer.watchDir=クラスタデプロイメントの監視[{0}]が変更されています。
fileMessageFactory.cannotRead=メッセージを読むことができません。このFactoryは書き込み中です。
fileMessageFactory.cannotWrite=メッセージを書き込めません、このFactoryは読み込み中です。
fileMessageFactory.closed=FileMessageFactoryはクローズされています。
fileMessageFactory.deleteFail=[{0}]を削除できませんでした
fileMessageFactory.duplicateMessage=メッセージをもう一度受信します。SenderのActTimeoutが短すぎます。 名前:[{0}] war[{1}] データ:[{2}] データ長:[{3}]
fileNewFail=[{0}]を作成できません。
warWatcher.cantListWatchDir=監視ディレクトリ [{0}] のファイル一覧を取得できません : ディレクトリの読み取り権限を確認してください。
warWatcher.checkWarResult=WAR ファイル [{1}] について WarInfo.check() は [{0}] を返しました。
warWatcher.checkingWar=WAR ファイル [{0}] をチェックしています。
warWatcher.checkingWars=[{0}]のWARを確認します
warWatcher.listedFileDoesNotExist=[{1}] にあるはずの [{0}] が存在しません。[{1}] のディレクトリ権限を確認してください。

View File

@@ -0,0 +1,58 @@
# 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.
farmWarDeployer.alreadyDeployed=webapp [{0}](들)이 이미 배치되어 있습니다.
farmWarDeployer.deleteFail=[{0}]을(를) 삭제하지 못했습니다.
farmWarDeployer.deployEnd=[{0}](으)로부터의 배치 작업이 완료됐습니다.
farmWarDeployer.fileCopyFail=[{0}](으)로부터 [{1}](으)로 복사할 수 없습니다.
farmWarDeployer.hostOnly=FarmWarDeployer는 오직 하위 엘리먼트인 호스트 클러스터 엘리먼트에서 존재해야 합니다.
farmWarDeployer.hostParentEngine=FarmWarDeployer는, 오직 [{0}]의 부모가 엔진일 때에만, 정상 동작할 수 있습니다!
farmWarDeployer.mbeanNameFail=엔진 [{0}]와(과) 호스트 [{1}]을(를) 위한 MBean 객체 이름을 구성할 수 없습니다.
farmWarDeployer.modInstall=[{1}](으)로부터 웹 애플리케이션 [{0}]을(를) 설치합니다.
farmWarDeployer.modInstallFail=WAR 파일을 설치할 수 없습니다.
farmWarDeployer.msgIoe=farm deploy 파일 메시지를 읽을 수 없습니다.
farmWarDeployer.msgRxDeploy=클러스터 배치 경로 [{0}]을(를) 받았음, war: [{1}]
farmWarDeployer.msgRxUndeploy=경로 [{0}]에 대한 클러스터 배치 제거 메시지를 수신했습니다.
farmWarDeployer.removeFailLocal=[{0}](으)로부터 로컬 삭제가 실패했습니다.
farmWarDeployer.removeFailRemote=FarmWarDeployer가 컨텍스트 [{0}]을(를) 로컬에서 제거하지 못하였습니다. 다른 Manager가 해당 애플리케이션을 서비스 중에 있습니다!
farmWarDeployer.removeLocal=웹 애플리케이션 [{0}]을(를) 제거합니다.
farmWarDeployer.removeLocalFail=WAR 파일을 제거할 수 없습니다.
farmWarDeployer.removeStart=웹 애플리케이션 [{0}]을(를) 전 클러스터에서 제거
farmWarDeployer.removeTxMsg=전 클러스터에서 [{0}]에 대한 배치를 제거 할 것을 전송합니다.
farmWarDeployer.renameFail=[{0}]을(를) [{1}](으)로 이름을 변경하지 못했습니다.
farmWarDeployer.sendEnd=클러스터 war 배치 경로 [{0}]을(를) 전송합니다. war [{1}]은(는) 완료되었습니다.
farmWarDeployer.sendFragment=클러스터 war 파일 (경로: [{0}]) [{1}]을(를) [{2}](으)로 전송합니다.
farmWarDeployer.sendStart=클러스터 war 배치 경로 [{0}]을(를) 전송합니다, war [{1}]이(가) 시작되었습니다.
farmWarDeployer.servicingDeploy=애플리케이션 [{0}]이(가) 서비스되고 있습니다. War 파일 [{1}]을(를) 다시 touch 하십시오!
farmWarDeployer.servicingUndeploy=애플리케이션 [{0}]이(가) 서비스 되고 있는 중이어서, 백업 클러스터 노드로부터 제거될 수 없습니다.
farmWarDeployer.started=클러스터 FarmWarDeployer가 시작되었습니다.
farmWarDeployer.stopped=클러스터 FarmWarDeployer가 중지되었습니다.
farmWarDeployer.undeployEnd=컨텍스트 [{0}]의 배치를 제거했습니다.
farmWarDeployer.undeployLocal=로컬 컨텍스트 [{0}]의 배치를 제거합니다.
farmWarDeployer.watchDir=클러스터 배치관리자가 변경사항들을 탐지하기 위해 [{0}]을(를) 감시합니다.
fileMessageFactory.cannotRead=팩토리가 쓰고 있는 중이라서, 메시지를 읽을 수 없습니다.
fileMessageFactory.cannotWrite=팩토리가 읽고 있는 중이라서, 메시지를 쓸 수 없습니다.
fileMessageFactory.closed=팩토리가 이미 닫혀 있습니다.
fileMessageFactory.deleteFail=[{0}]을(를) 삭제하지 못했습니다.
fileMessageFactory.duplicateMessage=중복된 메시지를 받았습니다. Sender의 제한 시간 초과 값이 너무 작게 설정되었나요? 컨텍스트: [{0}], 파일명: [{1}], 데이터: [{2}], 데이터 길이: [{3}]
fileNewFail=[{0}]을(를) 생성할 수 없습니다.
warWatcher.cantListWatchDir=WatchDir [{0}] 내의 파일 목록을 구할 수 없습니다. 해당 디렉토리가 존재하는지 그리고 읽기 권한이 있는지 점검하십시오.
warWatcher.checkWarResult=WarInfo.check()가 [{1}]을(를) 위해 [{0}]을(를) 반환했습니다.
warWatcher.checkingWar=WAR 파일 [{0}]을(를) 점검합니다.
warWatcher.checkingWars=[{0}] 내의 WAR들을 점검합니다.
warWatcher.listedFileDoesNotExist=[{0}]이(가) [{1}]에서 탐지되었으나, 존재하지 않습니다. [{1}]에 대한 디렉토리 접근 허가 설정을 점검해 보시겠습니까?

View File

@@ -0,0 +1,18 @@
# 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.
farmWarDeployer.modInstall=Установка веб приложения [{0}] из [{1}]
warWatcher.checkingWar=Проверяем WAR файл [{}]

View File

@@ -0,0 +1,38 @@
# 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.
farmWarDeployer.deleteFail=无法删除 [{0}]
farmWarDeployer.hostOnly=FarmWarDeployer 只有做为 host cluster 的子元素是才生效
farmWarDeployer.mbeanNameFail=无法为引擎[{0}]和主机[{1}]构造MBean对象名
farmWarDeployer.modInstall=从 [{1}] 安装 webapp [{0}]
farmWarDeployer.modInstallFail=无法安装 WAR 文件
farmWarDeployer.msgIoe=无法读取服务器场部署文件消息。
farmWarDeployer.msgRxDeploy=接收集群部署路径[{0}],战争[{1}]
farmWarDeployer.msgRxUndeploy=从路径[{0}]接收未部署群集
farmWarDeployer.removeFailLocal=从[{0}]本地移除失败
farmWarDeployer.removeFailRemote=本地从[{0}]删除失败其他经理有app在服务
farmWarDeployer.removeLocalFail=无法移除WAR文件
farmWarDeployer.renameFail=将 [{0}] 重命名为 [{1}] 失败
farmWarDeployer.sendFragment=将群集战争片段路径[{0}],战争[{1}]发送到[{2}]
farmWarDeployer.servicingDeploy=应用程序[{0}]正在服务。再次触摸WAR文件[{1}]
farmWarDeployer.servicingUndeploy=正在处理应用程序[{0}],无法从备份群集节点中删除它
farmWarDeployer.undeployEnd=从[{0}]取消部署完成。
farmWarDeployer.undeployLocal=不能部署本地上下文[{0}]
fileMessageFactory.deleteFail=无法删除 [{0}]
warWatcher.cantListWatchDir=无法列出WatchDir文件夹 [{0}] 中的文件:检查该路径是否为目录且应用具有读取权限。
warWatcher.checkWarResult=WarInfo.check() 为[{1}]返回[{0}]
warWatcher.checkingWar=检查 WAR 文件 [{0}]

View File

@@ -0,0 +1,69 @@
/*
* 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 org.apache.catalina.ha.ClusterMessage;
import org.apache.catalina.tribes.Member;
public class UndeployMessage implements ClusterMessage {
private static final long serialVersionUID = 2L;
private Member address;
private long timestamp;
private String uniqueId;
private final String contextName;
public UndeployMessage(Member address,
long timestamp,
String uniqueId,
String contextName) {
this.address = address;
this.timestamp= timestamp;
this.uniqueId = uniqueId;
this.contextName = contextName;
}
@Override
public Member getAddress() {
return address;
}
@Override
public void setAddress(Member address) {
this.address = address;
}
@Override
public long getTimestamp() {
return timestamp;
}
@Override
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
@Override
public String getUniqueId() {
return uniqueId;
}
public String getContextName() {
return contextName;
}
}

View File

@@ -0,0 +1,225 @@
/*
* 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.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
* The <b>WarWatcher </b> watches the deployDir for changes made to the
* directory (adding new WAR files-&gt;deploy or remove WAR files-&gt;undeploy)
* and notifies a listener of the changes made.
*
* @author Peter Rossbach
* @version 1.1
*/
public class WarWatcher {
/*--Static Variables----------------------------------------*/
private static final Log log = LogFactory.getLog(WarWatcher.class);
private static final StringManager sm = StringManager.getManager(WarWatcher.class);
/*--Instance Variables--------------------------------------*/
/**
* Directory to watch for war files
*/
protected final File watchDir;
/**
* Parent to be notified of changes
*/
protected final FileChangeListener listener;
/**
* Currently deployed files
*/
protected final Map<String, WarInfo> currentStatus = new HashMap<>();
/*--Constructor---------------------------------------------*/
public WarWatcher(FileChangeListener listener, File watchDir) {
this.listener = listener;
this.watchDir = watchDir;
}
/*--Logic---------------------------------------------------*/
/**
* check for modification and send notification to listener
*/
public void check() {
if (log.isDebugEnabled())
log.debug(sm.getString("warWatcher.checkingWars", watchDir));
File[] list = watchDir.listFiles(new WarFilter());
if (list == null) {
log.warn(sm.getString("warWatcher.cantListWatchDir",
watchDir));
list = new File[0];
}
//first make sure all the files are listed in our current status
for (int i = 0; i < list.length; i++) {
if(!list[i].exists())
log.warn(sm.getString("warWatcher.listedFileDoesNotExist",
list[i], watchDir));
addWarInfo(list[i]);
}
// Check all the status codes and update the FarmDeployer
for (Iterator<Map.Entry<String,WarInfo>> i =
currentStatus.entrySet().iterator(); i.hasNext();) {
Map.Entry<String,WarInfo> entry = i.next();
WarInfo info = entry.getValue();
if(log.isTraceEnabled())
log.trace(sm.getString("warWatcher.checkingWar",
info.getWar()));
int check = info.check();
if (check == 1) {
listener.fileModified(info.getWar());
} else if (check == -1) {
listener.fileRemoved(info.getWar());
//no need to keep in memory
i.remove();
}
if(log.isTraceEnabled())
log.trace(sm.getString("warWatcher.checkWarResult",
Integer.valueOf(check),
info.getWar()));
}
}
/**
* add cluster war to the watcher state
* @param warfile The WAR to add
*/
protected void addWarInfo(File warfile) {
WarInfo info = currentStatus.get(warfile.getAbsolutePath());
if (info == null) {
info = new WarInfo(warfile);
info.setLastState(-1); //assume file is non existent
currentStatus.put(warfile.getAbsolutePath(), info);
}
}
/**
* clear watcher state
*/
public void clear() {
currentStatus.clear();
}
/*--Inner classes-------------------------------------------*/
/**
* File name filter for war files
*/
protected static class WarFilter implements java.io.FilenameFilter {
@Override
public boolean accept(File path, String name) {
if (name == null)
return false;
return name.endsWith(".war");
}
}
/**
* File information on existing WAR files
*/
protected static class WarInfo {
protected final File war;
protected long lastChecked = 0;
protected long lastState = 0;
public WarInfo(File war) {
this.war = war;
this.lastChecked = war.lastModified();
if (!war.exists())
lastState = -1;
}
public boolean modified() {
return war.exists() && war.lastModified() > lastChecked;
}
public boolean exists() {
return war.exists();
}
/**
* Returns 1 if the file has been added/modified, 0 if the file is
* unchanged and -1 if the file has been removed
*
* @return int 1=file added; 0=unchanged; -1=file removed
*/
public int check() {
//file unchanged by default
int result = 0;
if (modified()) {
//file has changed - timestamp
result = 1;
lastState = result;
} else if ((!exists()) && (!(lastState == -1))) {
//file was removed
result = -1;
lastState = result;
} else if ((lastState == -1) && exists()) {
//file was added
result = 1;
lastState = result;
}
this.lastChecked = System.currentTimeMillis();
return result;
}
public File getWar() {
return war;
}
@Override
public int hashCode() {
return war.getAbsolutePath().hashCode();
}
@Override
public boolean equals(Object other) {
if (other instanceof WarInfo) {
WarInfo wo = (WarInfo) other;
return wo.getWar().equals(getWar());
} else {
return false;
}
}
protected void setLastState(int lastState) {
this.lastState = lastState;
}
}
}

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<mbeans-descriptors>
<mbean
name="FarmWarDeployer"
className="org.apache.catalina.mbeans.ClassNameMBean"
description="Farm Deployer"
domain="Catalina"
group="Cluster"
type="org.apache.catalina.ha.deploy.FarmWarDeployer">
<attribute
name="deployDir"
description="Deployment directory."
type="java.lang.String"/>
<attribute
name="tempDir"
description="The temporaryDirectory to store binary data when downloading a war from the cluster"
type="java.lang.String"/>
<attribute
name="watchDir"
description="The directory where we watch for changes"
type="java.lang.String"/>
<attribute
name="watchEnabled"
description="Is watching enabled?"
type="boolean"/>
<attribute
name="processDeployFrequency"
description="Frequency of the Farm watchDir check."
type="int"/>
<attribute
name="maxValidTime"
description="The maximum valid time of FileMessageFactory."
type="int"/>
</mbean>
</mbeans-descriptors>