mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-16 17:55:15 +00:00
8078: Merged V2.1 to V2.2 8025: Fixes WCM-1039, problems with case insensitive name handling. 8079: Merged V2.1 to V2.2 8035: -- DONE SEPARATELY -- 8040: Fix AR-1985: SQL Server dialect is derived from Sybase dialect there need additional no-op script 8046: Better Javadocs for getChildByName() 8056: Fixed WCM-790: Date conversion for metadata extractors 8057: Fixed WCM-790: Properties that don't convert can be discarded (default is to fail) 8059: -- DONE SEPARATELY -- 8061: Fixes WCM-790: Fallout from CHK-2168 and CHK-2169 8081: Fix for WCM-1018 8082: Merged V2.1 to V2.2 8016: Merged V2.1-A to V2.1 8000: Additional indexes for AVM 8013: Patch to introduce reverse indexes required for AVM git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@8474 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
1192 lines
47 KiB
Java
1192 lines
47 KiB
Java
/*
|
|
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
* As a special exception to the terms and conditions of version 2.0 of
|
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
|
* FLOSS exception. You should have recieved a copy of the text describing
|
|
* the FLOSS exception, and it is also available here:
|
|
* http://www.alfresco.com/legal/licensing"
|
|
*/
|
|
|
|
package org.alfresco.repo.deploy;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.SortedMap;
|
|
|
|
import org.alfresco.deployment.DeploymentReceiverService;
|
|
import org.alfresco.deployment.DeploymentReceiverTransport;
|
|
import org.alfresco.deployment.FileDescriptor;
|
|
import org.alfresco.deployment.FileType;
|
|
import org.alfresco.deployment.impl.client.DeploymentReceiverServiceClient;
|
|
import org.alfresco.repo.action.ActionServiceRemote;
|
|
import org.alfresco.repo.avm.AVMNodeConverter;
|
|
import org.alfresco.repo.avm.util.SimplePath;
|
|
import org.alfresco.repo.domain.PropertyValue;
|
|
import org.alfresco.repo.remote.AVMRemoteImpl;
|
|
import org.alfresco.repo.remote.AVMSyncServiceRemote;
|
|
import org.alfresco.repo.remote.ClientTicketHolder;
|
|
import org.alfresco.repo.remote.ClientTicketHolderThread;
|
|
import org.alfresco.service.cmr.action.ActionService;
|
|
import org.alfresco.service.cmr.action.ActionServiceTransport;
|
|
import org.alfresco.service.cmr.avm.AVMException;
|
|
import org.alfresco.service.cmr.avm.AVMNodeDescriptor;
|
|
import org.alfresco.service.cmr.avm.AVMNotFoundException;
|
|
import org.alfresco.service.cmr.avm.AVMService;
|
|
import org.alfresco.service.cmr.avm.AVMStoreDescriptor;
|
|
import org.alfresco.service.cmr.avm.AVMWrongTypeException;
|
|
import org.alfresco.service.cmr.avm.deploy.DeploymentCallback;
|
|
import org.alfresco.service.cmr.avm.deploy.DeploymentEvent;
|
|
import org.alfresco.service.cmr.avm.deploy.DeploymentReport;
|
|
import org.alfresco.service.cmr.avm.deploy.DeploymentService;
|
|
import org.alfresco.service.cmr.avmsync.AVMDifference;
|
|
import org.alfresco.service.cmr.avmsync.AVMSyncService;
|
|
import org.alfresco.service.cmr.remote.AVMRemote;
|
|
import org.alfresco.service.cmr.remote.AVMRemoteTransport;
|
|
import org.alfresco.service.cmr.remote.AVMSyncServiceTransport;
|
|
import org.alfresco.service.cmr.repository.ContentData;
|
|
import org.alfresco.service.cmr.security.AuthenticationService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.util.NameMatcher;
|
|
import org.alfresco.util.Pair;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.springframework.remoting.rmi.RmiProxyFactoryBean;
|
|
|
|
/**
|
|
* Implementation of DeploymentService.
|
|
* @author britt
|
|
*/
|
|
public class DeploymentServiceImpl implements DeploymentService
|
|
{
|
|
private static Log fgLogger = LogFactory.getLog(DeploymentServiceImpl.class);
|
|
|
|
/**
|
|
* Class to hold Deployment destination information.
|
|
* Used as a lock to serialize deployments to the same
|
|
* destination.
|
|
* @author britt
|
|
*/
|
|
private static class DeploymentDestination
|
|
{
|
|
private String fHost;
|
|
|
|
private int fPort;
|
|
|
|
DeploymentDestination(String host, int port)
|
|
{
|
|
fHost = host;
|
|
fPort = port;
|
|
}
|
|
|
|
/* (non-Javadoc)
|
|
* @see java.lang.Object#equals(java.lang.Object)
|
|
*/
|
|
@Override
|
|
public boolean equals(Object obj)
|
|
{
|
|
if (this == obj)
|
|
{
|
|
return true;
|
|
}
|
|
if (!(obj instanceof DeploymentDestination))
|
|
{
|
|
return false;
|
|
}
|
|
DeploymentDestination other = (DeploymentDestination)obj;
|
|
return fHost.equals(other.fHost) && fPort == other.fPort;
|
|
}
|
|
|
|
/* (non-Javadoc)
|
|
* @see java.lang.Object#hashCode()
|
|
*/
|
|
@Override
|
|
public int hashCode()
|
|
{
|
|
return fHost.hashCode() + fPort;
|
|
}
|
|
|
|
public String toString()
|
|
{
|
|
return fHost;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Holds locks for all deployment destinations (alfresco->alfresco)
|
|
*/
|
|
private Map<DeploymentDestination, DeploymentDestination> fDestinations;
|
|
|
|
/**
|
|
* The local AVMService Instance.
|
|
*/
|
|
private AVMService fAVMService;
|
|
|
|
/**
|
|
* The Ticket holder.
|
|
*/
|
|
private ClientTicketHolder fTicketHolder;
|
|
|
|
/**
|
|
* Default constructor.
|
|
*/
|
|
public DeploymentServiceImpl()
|
|
{
|
|
fTicketHolder = new ClientTicketHolderThread();
|
|
fDestinations = new HashMap<DeploymentDestination, DeploymentDestination>();
|
|
}
|
|
|
|
/**
|
|
* Setter.
|
|
* @param service The instance to set.
|
|
*/
|
|
public void setAvmService(AVMService service)
|
|
{
|
|
fAVMService = service;
|
|
}
|
|
|
|
/* (non-Javadoc)
|
|
* @see org.alfresco.service.cmr.avm.deploy.DeploymentService#deployDifference(int, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, boolean, boolean)
|
|
*/
|
|
public DeploymentReport deployDifference(int version, String srcPath, String hostName,
|
|
int port, String userName, String password,
|
|
String dstPath, NameMatcher matcher, boolean createDst,
|
|
boolean dontDelete, boolean dontDo,
|
|
List<DeploymentCallback> callbacks)
|
|
{
|
|
DeploymentDestination dest = getLock(hostName, port);
|
|
synchronized (dest)
|
|
{
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug("Deploying to Remote Alfresco at " + dest);
|
|
}
|
|
try
|
|
{
|
|
DeploymentReport report = new DeploymentReport();
|
|
AVMRemote remote = getRemote(hostName, port, userName, password);
|
|
if (callbacks != null)
|
|
{
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.START,
|
|
new Pair<Integer, String>(version, srcPath),
|
|
dstPath);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
if (version < 0)
|
|
{
|
|
String storeName = srcPath.substring(0, srcPath.indexOf(":"));
|
|
version = fAVMService.createSnapshot(storeName, null, null).get(storeName);
|
|
}
|
|
// Get the root of the deployment from this server.
|
|
AVMNodeDescriptor srcRoot = fAVMService.lookup(version, srcPath);
|
|
if (srcRoot == null)
|
|
{
|
|
throw new AVMNotFoundException("Directory Not Found: " + srcPath);
|
|
}
|
|
if (!srcRoot.isDirectory())
|
|
{
|
|
throw new AVMWrongTypeException("Not a directory: " + srcPath);
|
|
}
|
|
// Create a snapshot on the destination store.
|
|
String [] storePath = dstPath.split(":");
|
|
int snapshot = -1;
|
|
AVMNodeDescriptor dstParent = null;
|
|
if (!dontDo)
|
|
{
|
|
String[] parentBase = AVMNodeConverter.SplitBase(dstPath);
|
|
dstParent = remote.lookup(-1, parentBase[0]);
|
|
if (dstParent == null)
|
|
{
|
|
if (createDst)
|
|
{
|
|
createDestination(remote, parentBase[0]);
|
|
dstParent = remote.lookup(-1, parentBase[0]);
|
|
}
|
|
else
|
|
{
|
|
throw new AVMNotFoundException("Node Not Found: " + parentBase[0]);
|
|
}
|
|
}
|
|
snapshot = remote.createSnapshot(storePath[0], "PreDeploy", "Pre Deployment Snapshot").get(storePath[0]);
|
|
}
|
|
// Get the root of the deployment on the destination server.
|
|
AVMNodeDescriptor dstRoot = remote.lookup(-1, dstPath);
|
|
if (dstRoot == null)
|
|
{
|
|
// If it doesn't exist, do a copyDirectory to create it.
|
|
DeploymentEvent event =
|
|
new DeploymentEvent(DeploymentEvent.Type.COPIED,
|
|
new Pair<Integer, String>(version, srcPath),
|
|
dstPath);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
report.add(event);
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
if (dontDo)
|
|
{
|
|
return report;
|
|
}
|
|
copyDirectory(version, srcRoot, dstParent, remote, matcher);
|
|
remote.createSnapshot(storePath[0], "Deployment", "Post Deployment Snapshot.");
|
|
if (callbacks != null)
|
|
{
|
|
event = new DeploymentEvent(DeploymentEvent.Type.END,
|
|
new Pair<Integer, String>(version, srcPath),
|
|
dstPath);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
return report;
|
|
}
|
|
if (!dstRoot.isDirectory())
|
|
{
|
|
throw new AVMWrongTypeException("Not a Directory: " + dstPath);
|
|
}
|
|
// The corresponding directory exists so recursively deploy.
|
|
try
|
|
{
|
|
deployDirectoryPush(version, srcRoot, dstRoot, remote, matcher, dontDelete, dontDo, report, callbacks);
|
|
remote.createSnapshot(storePath[0], "Deployment", "Post Deployment Snapshot.");
|
|
if (callbacks != null)
|
|
{
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.END,
|
|
new Pair<Integer, String>(version, srcPath),
|
|
dstPath);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
return report;
|
|
}
|
|
catch (AVMException e)
|
|
{
|
|
if (callbacks != null)
|
|
{
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.FAILED,
|
|
new Pair<Integer, String>(version, srcPath),
|
|
dstPath, e.getMessage());
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
try
|
|
{
|
|
if (snapshot != -1)
|
|
{
|
|
AVMSyncService syncService = getSyncService(hostName, port);
|
|
List<AVMDifference> diffs = syncService.compare(snapshot, dstPath, -1, dstPath, null);
|
|
syncService.update(diffs, null, false, false, true, true, "Aborted Deployment", "Aborted Deployment");
|
|
}
|
|
}
|
|
catch (Exception ee)
|
|
{
|
|
throw new AVMException("Failed to rollback to version " + snapshot + " on " + hostName, ee);
|
|
}
|
|
throw new AVMException("Deployment to " + hostName + " failed.", e);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (callbacks != null)
|
|
{
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.FAILED,
|
|
new Pair<Integer, String>(version, srcPath),
|
|
dstPath, e.getMessage());
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
throw new AVMException("Deployment to " + hostName + " failed.", e);
|
|
}
|
|
finally
|
|
{
|
|
fTicketHolder.setTicket(null);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deploy all the children of corresponding directories.
|
|
* @param src The source directory.
|
|
* @param dst The destination directory.
|
|
* @param remote The AVMRemote instance.
|
|
* @param dontDelete Flag for not deleting.
|
|
* @param dontDo Flag for dry run.
|
|
*/
|
|
private void deployDirectoryPush(int version,
|
|
AVMNodeDescriptor src, AVMNodeDescriptor dst,
|
|
AVMRemote remote,
|
|
NameMatcher matcher,
|
|
boolean dontDelete, boolean dontDo,
|
|
DeploymentReport report,
|
|
List<DeploymentCallback> callbacks)
|
|
{
|
|
if (src.getGuid().equals(dst.getGuid()))
|
|
{
|
|
return;
|
|
}
|
|
if (!dontDo && !dontDelete)
|
|
{
|
|
copyMetadata(version, src, dst, remote);
|
|
}
|
|
// Get the listing for the source.
|
|
SortedMap<String, AVMNodeDescriptor> srcList = fAVMService.getDirectoryListing(src);
|
|
// Get the listing for the destination.
|
|
SortedMap<String, AVMNodeDescriptor> dstList = remote.getDirectoryListing(dst);
|
|
for (Map.Entry<String, AVMNodeDescriptor> entry : srcList.entrySet())
|
|
{
|
|
String name = entry.getKey();
|
|
AVMNodeDescriptor srcNode = entry.getValue();
|
|
AVMNodeDescriptor dstNode = dstList.get(name);
|
|
if (!excluded(matcher, srcNode.getPath(), dstNode != null ? dstNode.getPath() : null))
|
|
{
|
|
deploySinglePush(version, srcNode, dst, dstNode, remote, matcher, dontDelete, dontDo, report, callbacks);
|
|
}
|
|
}
|
|
// Delete nodes that are missing in the source.
|
|
if (dontDelete)
|
|
{
|
|
return;
|
|
}
|
|
for (String name : dstList.keySet())
|
|
{
|
|
if (!srcList.containsKey(name))
|
|
{
|
|
Pair<Integer, String> source =
|
|
new Pair<Integer, String>(version, AVMNodeConverter.ExtendAVMPath(src.getPath(), name));
|
|
String destination = AVMNodeConverter.ExtendAVMPath(dst.getPath(), name);
|
|
if (!excluded(matcher, null, destination))
|
|
{
|
|
DeploymentEvent event =
|
|
new DeploymentEvent(DeploymentEvent.Type.DELETED,
|
|
source,
|
|
destination);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
report.add(event);
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
if (dontDo)
|
|
{
|
|
continue;
|
|
}
|
|
remote.removeNode(dst.getPath(), name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Push out a single node.
|
|
* @param src The source node.
|
|
* @param dstParent The destination parent.
|
|
* @param dst The destination node. May be null.
|
|
* @param remote The AVMRemote instance.
|
|
* @param dontDelete Flag for whether deletions should happen.
|
|
* @param dontDo Dry run flag.
|
|
*/
|
|
private void deploySinglePush(int version,
|
|
AVMNodeDescriptor src, AVMNodeDescriptor dstParent,
|
|
AVMNodeDescriptor dst, AVMRemote remote,
|
|
NameMatcher matcher,
|
|
boolean dontDelete, boolean dontDo,
|
|
DeploymentReport report,
|
|
List<DeploymentCallback> callbacks)
|
|
{
|
|
// Destination does not exist.
|
|
if (dst == null)
|
|
{
|
|
if (src.isDirectory())
|
|
{
|
|
// Recursively copy a source directory.
|
|
Pair<Integer, String> source =
|
|
new Pair<Integer, String>(version, src.getPath());
|
|
String destination = AVMNodeConverter.ExtendAVMPath(dstParent.getPath(), src.getName());
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED,
|
|
source,
|
|
destination);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
report.add(event);
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
if (dontDo)
|
|
{
|
|
return;
|
|
}
|
|
copyDirectory(version, src, dstParent, remote, matcher);
|
|
return;
|
|
}
|
|
Pair<Integer, String> source =
|
|
new Pair<Integer, String>(version, src.getPath());
|
|
String destination = AVMNodeConverter.ExtendAVMPath(dstParent.getPath(), src.getName());
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED,
|
|
source,
|
|
destination);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
report.add(event);
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
if (dontDo)
|
|
{
|
|
return;
|
|
}
|
|
// Copy a source file.
|
|
OutputStream out = remote.createFile(dstParent.getPath(), src.getName());
|
|
InputStream in = fAVMService.getFileInputStream(src);
|
|
copyStream(in, out);
|
|
copyMetadata(version, src, remote.lookup(-1, dstParent.getPath() + '/' + src.getName()), remote);
|
|
return;
|
|
}
|
|
// Destination exists.
|
|
if (src.isDirectory())
|
|
{
|
|
// If the destination is also a directory, recursively deploy.
|
|
if (dst.isDirectory())
|
|
{
|
|
deployDirectoryPush(version, src, dst, remote, matcher, dontDelete, dontDo, report, callbacks);
|
|
return;
|
|
}
|
|
Pair<Integer, String> source =
|
|
new Pair<Integer, String>(version, src.getPath());
|
|
String destination = dst.getPath();
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED,
|
|
source, destination);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
report.add(event);
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
if (dontDo)
|
|
{
|
|
return;
|
|
}
|
|
remote.removeNode(dstParent.getPath(), src.getName());
|
|
copyDirectory(version, src, dstParent, remote, matcher);
|
|
return;
|
|
}
|
|
// Source is a file.
|
|
if (dst.isFile())
|
|
{
|
|
// Destination is also a file. Overwrite if the GUIDS are different.
|
|
if (src.getGuid().equals(dst.getGuid()))
|
|
{
|
|
return;
|
|
}
|
|
Pair<Integer, String> source =
|
|
new Pair<Integer, String>(version, src.getPath());
|
|
String destination = dst.getPath();
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.UPDATED,
|
|
source,
|
|
destination);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
report.add(event);
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
if (dontDo)
|
|
{
|
|
return;
|
|
}
|
|
InputStream in = fAVMService.getFileInputStream(src);
|
|
OutputStream out = remote.getFileOutputStream(dst.getPath());
|
|
copyStream(in, out);
|
|
copyMetadata(version, src, dst, remote);
|
|
return;
|
|
}
|
|
Pair<Integer, String> source =
|
|
new Pair<Integer, String>(version, src.getPath());
|
|
String destination = AVMNodeConverter.ExtendAVMPath(dstParent.getPath(), src.getName());
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.UPDATED,
|
|
source,
|
|
destination);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
report.add(event);
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
if (dontDo)
|
|
{
|
|
return;
|
|
}
|
|
// Destination is a directory and the source is a file.
|
|
// Delete the destination directory and copy the file over.
|
|
remote.removeNode(dstParent.getPath(), dst.getName());
|
|
InputStream in = fAVMService.getFileInputStream(src);
|
|
OutputStream out = remote.createFile(dstParent.getPath(), src.getName());
|
|
copyStream(in, out);
|
|
copyMetadata(version, src, remote.lookup(-1, dstParent.getPath() + '/' + dst.getName()), remote);
|
|
}
|
|
|
|
/**
|
|
* Recursively copy a directory.
|
|
* @param src
|
|
* @param parent
|
|
* @param remote
|
|
*/
|
|
private void copyDirectory(int version, AVMNodeDescriptor src, AVMNodeDescriptor parent,
|
|
AVMRemote remote, NameMatcher matcher)
|
|
{
|
|
// Create the destination directory.
|
|
remote.createDirectory(parent.getPath(), src.getName());
|
|
AVMNodeDescriptor newParent = remote.lookup(-1, parent.getPath() + '/' + src.getName());
|
|
copyMetadata(version, src, newParent, remote);
|
|
SortedMap<String, AVMNodeDescriptor> list =
|
|
fAVMService.getDirectoryListing(src);
|
|
// For each child in the source directory.
|
|
for (AVMNodeDescriptor child : list.values())
|
|
{
|
|
if (!excluded(matcher, child.getPath(), null))
|
|
{
|
|
// If it's a file, copy it over and move on.
|
|
if (child.isFile())
|
|
{
|
|
InputStream in = fAVMService.getFileInputStream(child);
|
|
OutputStream out = remote.createFile(newParent.getPath(), child.getName());
|
|
copyStream(in, out);
|
|
copyMetadata(version, child, remote.lookup(-1, newParent.getPath() + '/' + child.getName()), remote);
|
|
continue;
|
|
}
|
|
// Otherwise copy the child directory recursively.
|
|
copyDirectory(version, child, newParent, remote, matcher);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility for copying from one stream to another.
|
|
* @param in The input stream.
|
|
* @param out The output stream.
|
|
*/
|
|
private void copyStream(InputStream in, OutputStream out)
|
|
{
|
|
byte[] buff = new byte[8192];
|
|
int read = 0;
|
|
try
|
|
{
|
|
while ((read = in.read(buff)) != -1)
|
|
{
|
|
out.write(buff, 0, read);
|
|
}
|
|
in.close();
|
|
out.close();
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
throw new AVMException("I/O Exception", e);
|
|
}
|
|
}
|
|
|
|
private void copyMetadata(int version, AVMNodeDescriptor src, AVMNodeDescriptor dst, AVMRemote remote)
|
|
{
|
|
Map<QName, PropertyValue> props = fAVMService.getNodeProperties(version, src.getPath());
|
|
remote.setNodeProperties(dst.getPath(), props);
|
|
Set<QName> aspects = fAVMService.getAspects(version, src.getPath());
|
|
for (QName aspect : aspects)
|
|
{
|
|
if (remote.hasAspect(-1, dst.getPath(), aspect))
|
|
{
|
|
continue;
|
|
}
|
|
remote.addAspect(dst.getPath(), aspect);
|
|
}
|
|
remote.setGuid(dst.getPath(), src.getGuid());
|
|
if (src.isFile())
|
|
{
|
|
ContentData contData = fAVMService.getContentDataForRead(version, src.getPath());
|
|
remote.setEncoding(dst.getPath(), contData.getEncoding());
|
|
remote.setMimeType(dst.getPath(), contData.getMimetype());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility to get an AVMRemote from a remote Alfresco Server.
|
|
* @param hostName
|
|
* @param port
|
|
* @param userName
|
|
* @param password
|
|
* @return
|
|
*/
|
|
private AVMRemote getRemote(String hostName, int port, String userName, String password)
|
|
{
|
|
try
|
|
{
|
|
RmiProxyFactoryBean authFactory = new RmiProxyFactoryBean();
|
|
authFactory.setRefreshStubOnConnectFailure(true);
|
|
authFactory.setServiceInterface(AuthenticationService.class);
|
|
authFactory.setServiceUrl("rmi://" + hostName + ":" + port + "/authentication");
|
|
authFactory.afterPropertiesSet();
|
|
AuthenticationService authService = (AuthenticationService)authFactory.getObject();
|
|
authService.authenticate(userName, password.toCharArray());
|
|
String ticket = authService.getCurrentTicket();
|
|
fTicketHolder.setTicket(ticket);
|
|
RmiProxyFactoryBean remoteFactory = new RmiProxyFactoryBean();
|
|
remoteFactory.setRefreshStubOnConnectFailure(true);
|
|
remoteFactory.setServiceInterface(AVMRemoteTransport.class);
|
|
remoteFactory.setServiceUrl("rmi://" + hostName + ":" + port + "/avm");
|
|
remoteFactory.afterPropertiesSet();
|
|
AVMRemoteTransport transport = (AVMRemoteTransport)remoteFactory.getObject();
|
|
AVMRemoteImpl remote = new AVMRemoteImpl();
|
|
remote.setAvmRemoteTransport(transport);
|
|
remote.setClientTicketHolder(fTicketHolder);
|
|
return remote;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new AVMException("Could not Initialize Remote Connection to " + hostName, e);
|
|
}
|
|
}
|
|
|
|
/* (non-Javadoc)
|
|
* @see org.alfresco.service.cmr.avm.deploy.DeploymentService#getRemoteActionService(java.lang.String, int, java.lang.String, java.lang.String)
|
|
*/
|
|
public ActionService getRemoteActionService(String hostName, int port, String userName, String password)
|
|
{
|
|
try
|
|
{
|
|
RmiProxyFactoryBean authFactory = new RmiProxyFactoryBean();
|
|
authFactory.setRefreshStubOnConnectFailure(true);
|
|
authFactory.setServiceInterface(AuthenticationService.class);
|
|
authFactory.setServiceUrl("rmi://" + hostName + ":" + port + "/authentication");
|
|
authFactory.afterPropertiesSet();
|
|
AuthenticationService authService = (AuthenticationService)authFactory.getObject();
|
|
authService.authenticate(userName, password.toCharArray());
|
|
String ticket = authService.getCurrentTicket();
|
|
fTicketHolder.setTicket(ticket);
|
|
RmiProxyFactoryBean remoteFactory = new RmiProxyFactoryBean();
|
|
remoteFactory.setRefreshStubOnConnectFailure(true);
|
|
remoteFactory.setServiceInterface(ActionServiceTransport.class);
|
|
remoteFactory.setServiceUrl("rmi://" + hostName + ":" + port + "/action");
|
|
remoteFactory.afterPropertiesSet();
|
|
ActionServiceTransport transport = (ActionServiceTransport)remoteFactory.getObject();
|
|
ActionServiceRemote remote = new ActionServiceRemote();
|
|
remote.setActionServiceTransport(transport);
|
|
remote.setClientTicketHolder(fTicketHolder);
|
|
return remote;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new AVMException("Could not Initialize Remote Connection to " + hostName, e);
|
|
}
|
|
}
|
|
|
|
private DeploymentReceiverService getReceiver(String hostName, int port)
|
|
{
|
|
try
|
|
{
|
|
RmiProxyFactoryBean factory = new RmiProxyFactoryBean();
|
|
factory.setRefreshStubOnConnectFailure(true);
|
|
factory.setServiceInterface(DeploymentReceiverTransport.class);
|
|
factory.setServiceUrl("rmi://" + hostName + ":" + port + "/deployment");
|
|
factory.afterPropertiesSet();
|
|
DeploymentReceiverTransport transport = (DeploymentReceiverTransport)factory.getObject();
|
|
DeploymentReceiverServiceClient service = new DeploymentReceiverServiceClient();
|
|
service.setDeploymentReceiverTransport(transport);
|
|
return service;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new AVMException("Could not connect to " + hostName + " at " + port, e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility to get the sync service for rolling back after a failed deployment.
|
|
* @param hostName The target machine.
|
|
* @param port The port.
|
|
* @return An AVMSyncService instance.
|
|
*/
|
|
private AVMSyncService getSyncService(String hostName, int port)
|
|
{
|
|
try
|
|
{
|
|
RmiProxyFactoryBean syncFactory = new RmiProxyFactoryBean();
|
|
syncFactory.setRefreshStubOnConnectFailure(true);
|
|
syncFactory.setServiceInterface(AVMSyncServiceTransport.class);
|
|
syncFactory.setServiceUrl("rmi://" + hostName + ":" + port + "/avmsync");
|
|
syncFactory.afterPropertiesSet();
|
|
AVMSyncServiceTransport syncServiceTransport = (AVMSyncServiceTransport)syncFactory.getObject();
|
|
AVMSyncServiceRemote remote = new AVMSyncServiceRemote();
|
|
remote.setAvmSyncServiceTransport(syncServiceTransport);
|
|
remote.setClientTicketHolder(fTicketHolder);
|
|
return remote;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new AVMException("Could not roll back failed deployment to " + hostName, e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper function to create a non existent destination.
|
|
* @param remote The AVMRemote instance.
|
|
* @param dstPath The destination path to create.
|
|
*/
|
|
private void createDestination(AVMRemote remote, String dstPath)
|
|
{
|
|
String[] storePath = dstPath.split(":");
|
|
String storeName = storePath[0];
|
|
String path = storePath[1];
|
|
AVMStoreDescriptor storeDesc = remote.getStore(storeName);
|
|
if (storeDesc == null)
|
|
{
|
|
remote.createStore(storeName);
|
|
}
|
|
SimplePath simpPath = new SimplePath(path);
|
|
if (simpPath.size() == 0)
|
|
{
|
|
return;
|
|
}
|
|
String prevPath = storeName + ":/";
|
|
for (int i = 0; i < simpPath.size(); i++)
|
|
{
|
|
String currPath = AVMNodeConverter.ExtendAVMPath(prevPath, simpPath.get(i));
|
|
AVMNodeDescriptor desc = remote.lookup(-1, currPath);
|
|
if (desc == null)
|
|
{
|
|
remote.createDirectory(prevPath, simpPath.get(i));
|
|
}
|
|
prevPath = currPath;
|
|
}
|
|
}
|
|
|
|
/* (non-Javadoc)
|
|
* @see org.alfresco.service.cmr.avm.deploy.DeploymentService#deployDifferenceFS(int, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, boolean, boolean)
|
|
*/
|
|
public DeploymentReport deployDifferenceFS(int version, String srcPath, String hostName,
|
|
int port, String userName, String password,
|
|
String target, NameMatcher matcher, boolean createDst,
|
|
boolean dontDelete, boolean dontDo,
|
|
List<DeploymentCallback> callbacks)
|
|
{
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug("Deploying To FileSystem Reciever on " + hostName + " to target " + target);
|
|
}
|
|
DeploymentReport report = new DeploymentReport();
|
|
DeploymentReceiverService service = null;
|
|
String ticket = null;
|
|
try
|
|
{
|
|
service = getReceiver(hostName, port);
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.START,
|
|
new Pair<Integer, String>(version, srcPath),
|
|
target);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
report.add(event);
|
|
String storeName = srcPath.substring(0, srcPath.indexOf(':'));
|
|
System.out.println(storeName);
|
|
if (version < 0)
|
|
{
|
|
version = fAVMService.createSnapshot(storeName, null, null).get(storeName);
|
|
}
|
|
ticket = service.begin(target, userName, password);
|
|
deployDirectoryPush(service, ticket, report, callbacks, version, srcPath, "/", matcher);
|
|
service.commit(ticket);
|
|
event = new DeploymentEvent(DeploymentEvent.Type.END,
|
|
new Pair<Integer, String>(version, srcPath),
|
|
target);
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
report.add(event);
|
|
return report;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.FAILED,
|
|
new Pair<Integer, String>(version, srcPath),
|
|
target, e.getMessage());
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
|
|
if (service != null)
|
|
{
|
|
service.abort(ticket);
|
|
}
|
|
|
|
throw new AVMException("Deployment to: " + target + " failed.", e);
|
|
}
|
|
}
|
|
|
|
private void deployDirectoryPush(DeploymentReceiverService service, String ticket,
|
|
DeploymentReport report, List<DeploymentCallback> callbacks,
|
|
int version,
|
|
String srcPath, String dstPath, NameMatcher matcher)
|
|
{
|
|
Map<String, AVMNodeDescriptor> srcListing = fAVMService.getDirectoryListing(version, srcPath);
|
|
List<FileDescriptor> dstListing = service.getListing(ticket, dstPath);
|
|
Iterator<AVMNodeDescriptor> srcIter = srcListing.values().iterator();
|
|
Iterator<FileDescriptor> dstIter = dstListing.iterator();
|
|
AVMNodeDescriptor src = null;
|
|
FileDescriptor dst = null;
|
|
while (srcIter.hasNext() || dstIter.hasNext())
|
|
{
|
|
if (src == null)
|
|
{
|
|
if (srcIter.hasNext())
|
|
{
|
|
src = srcIter.next();
|
|
}
|
|
}
|
|
if (dst == null)
|
|
{
|
|
if (dstIter.hasNext())
|
|
{
|
|
dst = dstIter.next();
|
|
}
|
|
}
|
|
// This means no entry on src so delete.
|
|
if (src == null)
|
|
{
|
|
String newDstPath = extendPath(dstPath, dst.getName());
|
|
if (!excluded(matcher, null, newDstPath))
|
|
{
|
|
service.delete(ticket, newDstPath);
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.DELETED,
|
|
new Pair<Integer, String>(version, extendPath(srcPath, dst.getName())),
|
|
newDstPath);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
report.add(event);
|
|
}
|
|
dst = null;
|
|
continue;
|
|
}
|
|
// Nothing on the destination so copy over.
|
|
if (dst == null)
|
|
{
|
|
if (!excluded(matcher, src.getPath(), null))
|
|
{
|
|
copy(service, ticket, report, callbacks, version, src, dstPath, matcher);
|
|
}
|
|
src = null;
|
|
continue;
|
|
}
|
|
int diff = src.getName().compareToIgnoreCase(dst.getName());
|
|
if (diff < 0)
|
|
{
|
|
if (!excluded(matcher, src.getPath(), null))
|
|
{
|
|
copy(service, ticket, report, callbacks, version, src, dstPath, matcher);
|
|
}
|
|
src = null;
|
|
continue;
|
|
}
|
|
if (diff == 0)
|
|
{
|
|
if (src.getGuid().equals(dst.getGUID()))
|
|
{
|
|
src = null;
|
|
dst = null;
|
|
continue;
|
|
}
|
|
if (src.isFile())
|
|
{
|
|
String extendedPath = extendPath(dstPath, dst.getName());
|
|
if (!excluded(matcher, src.getPath(), extendedPath))
|
|
{
|
|
copyFile(service, ticket, report, callbacks, version, src,
|
|
extendedPath);
|
|
}
|
|
src = null;
|
|
dst = null;
|
|
continue;
|
|
}
|
|
// Source is a directory.
|
|
if (dst.getType() == FileType.DIR)
|
|
{
|
|
String extendedPath = extendPath(dstPath, dst.getName());
|
|
if (!excluded(matcher, src.getPath(), extendedPath))
|
|
{
|
|
deployDirectoryPush(service, ticket, report, callbacks, version, src.getPath(), extendPath(dstPath, dst.getName()), matcher);
|
|
}
|
|
service.setGuid(ticket, extendedPath, src.getGuid());
|
|
src = null;
|
|
dst = null;
|
|
continue;
|
|
}
|
|
if (!excluded(matcher, src.getPath(), null))
|
|
{
|
|
copy(service, ticket, report, callbacks, version, src, dstPath, matcher);
|
|
}
|
|
src = null;
|
|
dst = null;
|
|
continue;
|
|
}
|
|
// diff > 0
|
|
// Destination is missing in source, delete it.
|
|
String newDstPath = extendPath(dstPath, dst.getName());
|
|
service.delete(ticket, newDstPath);
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.DELETED,
|
|
new Pair<Integer, String>(version, extendPath(srcPath, dst.getName())),
|
|
newDstPath);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
report.add(event);
|
|
dst = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy or overwrite a single file.
|
|
* @param service
|
|
* @param ticket
|
|
* @param report
|
|
* @param callback
|
|
* @param version
|
|
* @param src
|
|
* @param dstPath
|
|
*/
|
|
private void copyFile(DeploymentReceiverService service, String ticket,
|
|
DeploymentReport report, List<DeploymentCallback> callbacks, int version,
|
|
AVMNodeDescriptor src, String dstPath)
|
|
{
|
|
InputStream in = fAVMService.getFileInputStream(src);
|
|
OutputStream out = service.send(ticket, dstPath, src.getGuid());
|
|
try
|
|
{
|
|
copyStream(in, out);
|
|
service.finishSend(ticket, out);
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED,
|
|
new Pair<Integer, String>(version, src.getPath()),
|
|
dstPath);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
report.add(event);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
service.abort(ticket);
|
|
throw new AVMException("Failed to copy " + src + ". Deployment aborted.", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy a file or directory to an empty destination.
|
|
* @param service
|
|
* @param ticket
|
|
* @param report
|
|
* @param callback
|
|
* @param version
|
|
* @param src
|
|
* @param parentPath
|
|
*/
|
|
private void copy(DeploymentReceiverService service, String ticket,
|
|
DeploymentReport report, List<DeploymentCallback> callbacks,
|
|
int version, AVMNodeDescriptor src, String parentPath, NameMatcher matcher)
|
|
{
|
|
String dstPath = extendPath(parentPath, src.getName());
|
|
if (src.isFile())
|
|
{
|
|
copyFile(service, ticket, report, callbacks, version, src, dstPath);
|
|
return;
|
|
}
|
|
// src is a directory.
|
|
service.mkdir(ticket, dstPath, src.getGuid());
|
|
DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED,
|
|
new Pair<Integer, String>(version, src.getPath()),
|
|
dstPath);
|
|
if (fgLogger.isDebugEnabled())
|
|
{
|
|
fgLogger.debug(event);
|
|
}
|
|
if (callbacks != null)
|
|
{
|
|
for (DeploymentCallback callback : callbacks)
|
|
{
|
|
callback.eventOccurred(event);
|
|
}
|
|
}
|
|
report.add(event);
|
|
Map<String, AVMNodeDescriptor> listing = fAVMService.getDirectoryListing(src);
|
|
for (AVMNodeDescriptor child : listing.values())
|
|
{
|
|
if (!excluded(matcher, child.getPath(), null))
|
|
{
|
|
copy(service, ticket, report, callbacks, version, child, dstPath, matcher);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extend a path.
|
|
* @param path
|
|
* @param name
|
|
* @return
|
|
*/
|
|
private String extendPath(String path, String name)
|
|
{
|
|
if (path.endsWith("/"))
|
|
{
|
|
return path + name;
|
|
}
|
|
return path + '/' + name;
|
|
}
|
|
|
|
/**
|
|
* Returns true if either srcPath or dstPath are matched by matcher.
|
|
* @param matcher
|
|
* @param srcPath
|
|
* @param dstPath
|
|
* @return
|
|
*/
|
|
private boolean excluded(NameMatcher matcher, String srcPath, String dstPath)
|
|
{
|
|
return matcher != null && ((srcPath != null && matcher.matches(srcPath)) || (dstPath != null && matcher.matches(dstPath)));
|
|
}
|
|
|
|
/**
|
|
* Get the object to lock for an alfresco->alfresco target.
|
|
* @param host
|
|
* @param port
|
|
* @return
|
|
*/
|
|
private synchronized DeploymentDestination getLock(String host, int port)
|
|
{
|
|
DeploymentDestination newDest = new DeploymentDestination(host, port);
|
|
DeploymentDestination dest = fDestinations.get(newDest);
|
|
if (dest == null)
|
|
{
|
|
dest = newDest;
|
|
fDestinations.put(dest, dest);
|
|
}
|
|
return dest;
|
|
}
|
|
}
|