/*******************************************************************************
* Copyright (c) 2009, 2010 Innovation Gate GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Innovation Gate GmbH - initial API and implementation
******************************************************************************/
package de.innovationgate.eclipse.wgadesigner.team;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.subscribers.Subscriber;
import org.eclipse.team.core.subscribers.SubscriberChangeEvent;
import org.eclipse.team.core.synchronize.SyncInfo;
import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.team.core.variants.IResourceVariantComparator;
import de.innovationgate.eclipse.utils.wga.WGADesignStructureHelper;
import de.innovationgate.eclipse.wgadesigner.WGADesignerPlugin;
import de.innovationgate.eclipse.wgadesigner.editors.helpers.WGADeployment;
import de.innovationgate.eclipse.wgadesigner.editors.helpers.WGADeploymentManager;
import de.innovationgate.eclipse.wgadesigner.models.WGADistribution;
import de.innovationgate.eclipse.wgadesigner.models.WGARemoteServer;
import de.innovationgate.eclipse.wgadesigner.natures.WGARuntime;
import de.innovationgate.eclipse.wgadesigner.tomcat.TomcatUtils;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.wgaservices.types.FSDesignResourceState;
import de.innovationgate.wgaservices.types.PluginInfo;
import de.innovationgate.wgaservices.types.Version;
public class WGARemoteServerSubscriber extends Subscriber {
private WGAFSDesignResourceVariantComparator _comparator;
private WGAPluginResourceVariantComparator _pluginComparator;
private WGARuntime _runtime;
private WGARemoteServer _server;
// local and remote states mapped by their paths relative to the wga designroot
private Map<IPath, LocalFSDesignResourceState> _localStates = new HashMap<IPath, LocalFSDesignResourceState>();
private Map<IPath, FSDesignResourceState> _remoteStates = new HashMap<IPath, FSDesignResourceState>();
// local and remote states mapped by resource
private Map<IResource, LocalFSDesignResourceState> _localStatesByResource =new HashMap<IResource, LocalFSDesignResourceState>();
private Map<IResource, FSDesignResourceState> _remoteStatesByResource =new HashMap<IResource, FSDesignResourceState>();
private Map<IResource, PluginInfo> _localPluginInfos = new HashMap<IResource, PluginInfo>();
private Map<IResource, PluginInfo> _remotePluginInfos = new HashMap<IResource, PluginInfo>();
// lock for update progress
private Object _refreshLock = new Object();
private Version _remoteWGAVersion;
public WGARemoteServerSubscriber(WGARemoteServer server, WGARuntime runtime) {
_server = server;
_runtime = runtime;
_comparator = new WGAFSDesignResourceVariantComparator();
_pluginComparator = new WGAPluginResourceVariantComparator();
refresh(roots(), IResource.DEPTH_INFINITE, new NullProgressMonitor());
}
public String getName() {
return "Subscriber for '" + _runtime.getName() + "'.";
}
public boolean isSupervised(IResource resource) {
synchronized (_refreshLock) {
return _localStatesByResource.containsKey(resource) || _remoteStatesByResource.containsKey(resource) || isPluginResource(resource);
}
// if (resource.getProject().equals(_runtime.getProject())) {
// return _runtime.getDesignRoot().getFullPath().isPrefixOf(resource.getFullPath());
// } else {
// return false;
// }
}
public IResource[] members(IResource resource) throws TeamException {
synchronized (_refreshLock ) {
Map<IPath, IResource> existingChildren = new HashMap<IPath, IResource>();
for (IPath path : _localStates.keySet()) {
if (isAffected(path, true)) {
IResource child = convertToResource(_localStates.get(path));
if (child != null) {
_localStatesByResource.put(child, _localStates.get(path));
// add highest parent
IResource parent = child;
while (parent != null) {
IResource grandParent = parent.getParent();
if (grandParent != null && grandParent.getFullPath().equals(resource.getFullPath())) {
existingChildren.put(parent.getFullPath(), parent);
break;
}
parent = grandParent;
}
}
}
}
for (IPath path : _remoteStates.keySet()) {
if (isAffected(path, false)) {
IResource child = convertToResource(_remoteStates.get(path));
if (child != null) {
_remoteStatesByResource.put(child, _remoteStates.get(path));
// add highest parent
IResource parent = child;
while (parent != null) {
IResource grandParent = parent.getParent();
if (grandParent != null && grandParent.getFullPath().equals(resource.getFullPath())) {
existingChildren.put(parent.getFullPath(), parent);
break;
}
parent = grandParent;
}
}
}
}
IResource virtualPluginRoot = retrieveVirtualPluginRoot(_runtime);
if (resource instanceof IProject) {
existingChildren.put(virtualPluginRoot.getFullPath(), virtualPluginRoot);
} else if (resource.getFullPath().equals(virtualPluginRoot.getFullPath())) {
for (IResource virtualPluginResource : _localPluginInfos.keySet()) {
existingChildren.put(virtualPluginResource.getFullPath(), virtualPluginResource);
}
for (IResource virtualPluginResource : _remotePluginInfos.keySet()) {
existingChildren.put(virtualPluginResource.getFullPath(), virtualPluginResource);
}
}
return (IResource[]) existingChildren.values().toArray(new IResource[existingChildren.size()]);
}
}
/**
* checks if the given design resource path has a change
* @param path path rooted at the wga design root
* @param local true if this is a local path / false if path is remote
* @return
*/
private boolean isAffected(IPath path, boolean local) {
FSDesignResourceState stateA = null;
FSDesignResourceState stateB = null;
if (local) {
stateA = _localStates.get(path);
stateB = _remoteStates.get(path);
} else {
stateA = _remoteStates.get(path);
stateB = _localStates.get(path);
}
if (stateA == null) {
return false;
}
if (stateA.getType() == FSDesignResourceState.TYPE_FILE) {
return (stateB == null || stateB.getLastmodified() != stateA.getLastmodified());
} else if (stateA.getType() == FSDesignResourceState.TYPE_FOLDER) {
if (stateB == null) {
// this is a folder deletion
return true;
} else {
// check if child folders or files are affected
for (IPath childPath : _localStates.keySet()) {
if (path.isPrefixOf(childPath) && !path.equals(childPath)) {
boolean childIsAffected = isAffected(childPath, local);
if (childIsAffected) {
return true;
}
}
}
}
}
return false;
}
public IResource[] roots() {
List<IResource> roots = new ArrayList<IResource>();
roots.add(_runtime.getProject());
IFolder[] designFolders = _runtime.getDesignsAsFolder(false);
for (IFolder designFolder : designFolders) {
if (WGADesignStructureHelper.isDirlinkFolder(designFolder)) {
IContainer dirlinkTarget = WGADesignStructureHelper.resolveDirLink(designFolder.getFile(WGUtils.DIRLINK_FILE));
roots.add(dirlinkTarget.getProject());
}
}
return roots.toArray(new IResource[0]);
}
public SyncInfo getSyncInfo(IResource resource) throws TeamException {
synchronized (_refreshLock) {
try {
if (isPluginResource(resource)) {
IResourceVariant variant = null;
PluginInfo remotePluginInfo = _remotePluginInfos.get(resource);
if (remotePluginInfo != null) {
variant = new WGAPluginResourceVariant(_server, remotePluginInfo);
}
PluginInfo localPluginInfo = _localPluginInfos.get(resource);
SyncInfo info = new WGAPluginResourceSyncInfo(_runtime, _server, resource, localPluginInfo, variant, _remoteWGAVersion);
info.init();
return info;
} else {
IResourceVariant variant = null;
FSDesignResourceState state = _remoteStatesByResource.get(resource);
if (state != null) {
variant = new WGAFSDesignResourceVariant(_server, state);
}
LocalFSDesignResourceState localState = _localStatesByResource.get(resource);
SyncInfo info = new WGAFSDesignResourceSyncInfo(_runtime, _server, resource, localState, variant, variant, _comparator);
info.init();
return info;
}
} catch (CoreException e) {
throw TeamException.asTeamException(e);
}
}
}
private boolean isPluginResource(IResource resource) {
if (resource != null && resource.getProject().getFolder("plugins").getFullPath().isPrefixOf(resource.getFullPath())) {
return true;
}
return false;
}
public IResourceVariantComparator getResourceComparator() {
return _comparator;
}
public void refresh(IResource[] resources, int depth, IProgressMonitor monitor) {
synchronized (_refreshLock) {
try {
_localStates.clear();
_remoteStates.clear();
_localStatesByResource.clear();
_remoteStatesByResource.clear();
_localPluginInfos.clear();
_remotePluginInfos.clear();
monitor.beginTask("retrieving local resource states", IProgressMonitor.UNKNOWN);
IResource start = _runtime.getDesignRoot();
List<LocalFSDesignResourceState> localStates = buildFSDesignResourceStates(start, _runtime.getDesignRoot().getFullPath());
//print(localStates);
monitor.setTaskName("connecting to remote server '" + _server.getName() + "'.");
_server.connectToServer();
monitor.setTaskName("retrieving remote resouce states");
List<FSDesignResourceState> remoteStates = _server.getServices().retrieveFSDesignResourceState(_server.getSession(), computeRelativPathFromDesignRoot(start).toString());
// map states by path rooted to wga designroot
for (LocalFSDesignResourceState state : localStates) {
IPath path = new Path(state.getPath());
_localStates.put(path.makeRelative(), state);
}
for (FSDesignResourceState state : remoteStates) {
IPath path = new Path(state.getPath());
_remoteStates.put(path.makeRelative(), state);
}
// perform steps for plugin sync if runtime is started
if (TomcatUtils.getInstance().isRunning(_runtime)) {
monitor.beginTask("connecting to local WGA server", IProgressMonitor.UNKNOWN);
WGARemoteServer local = _runtime.createRemoteServer();
local.connectToServer();
Version localWGAVersion = null;
try {
monitor.beginTask("retrieving local WGA server version", IProgressMonitor.UNKNOWN);
localWGAVersion = local.getServices().getWGAVersion(local.getSession());
}
catch (Exception e) {
// this might happen if server version is < 5.3
}
try {
monitor.beginTask("retrieving remote WGA server version", IProgressMonitor.UNKNOWN);
_remoteWGAVersion = _server.getServices().getWGAVersion(_server.getSession());
}
catch (Exception e) {
// this might happen if server version is < 5.3
}
// check local and remote WGA version - plugin sync is available for WGA >= 5.3
if (localWGAVersion != null && localWGAVersion.isAtLeast(5, 3) && _remoteWGAVersion != null && _remoteWGAVersion.isAtLeast(5, 3)) {
monitor.beginTask("retrieving local plugin infos", IProgressMonitor.UNKNOWN);
List<PluginInfo> infos = local.getServices().getPluginInformation(local.getSession());
for (PluginInfo info : infos) {
// we will sync only active & valid plugins ... dev plugins & platform plugins are excluded
if (info.isActive() && info.isValid() && !info.isPlatformPlugin() && !info.isDeveloperPlugin()) {
IResource virtualPluginResource = buildVirtualPluginResource(_runtime, info);
_localPluginInfos.put(virtualPluginResource, info);
}
}
monitor.beginTask("retrieving remote plugin infos", IProgressMonitor.UNKNOWN);
infos = _server.getServices().getPluginInformation(_server.getSession());
for (PluginInfo info : infos) {
// we will sync only active & valid plugins ... dev plugins & platform plugins are excluded
if (info.isActive() && info.isValid() && !info.isPlatformPlugin() && !info.isDeveloperPlugin()) {
IResource virtualPluginResource = buildVirtualPluginResource(_runtime, info);
_remotePluginInfos.put(virtualPluginResource, info);
}
}
}
}
// fire subscriber events to notify listeners ofs syncstate change
List<SubscriberChangeEvent> events = new ArrayList<SubscriberChangeEvent>();
for (IResource root : roots()) {
SubscriberChangeEvent eventA = new SubscriberChangeEvent(this, SubscriberChangeEvent.ROOT_REMOVED, root);
SubscriberChangeEvent eventB = new SubscriberChangeEvent(this, SubscriberChangeEvent.ROOT_ADDED, root);
events.add(eventA);
events.add(eventB);
}
fireTeamResourceChange(events.toArray(new SubscriberChangeEvent[0]));
} catch (Exception e) {
List<SubscriberChangeEvent> events = new ArrayList<SubscriberChangeEvent>();
for (IResource root : roots()) {
SubscriberChangeEvent eventA = new SubscriberChangeEvent(this, SubscriberChangeEvent.ROOT_REMOVED, root);
events.add(eventA);
}
fireTeamResourceChange(events.toArray(new SubscriberChangeEvent[0]));
throw new RuntimeException(e);
} finally {
monitor.done();
}
}
}
private List<LocalFSDesignResourceState> buildFSDesignResourceStates(IResource resource, IPath base) throws CoreException {
List<LocalFSDesignResourceState> states = new ArrayList<LocalFSDesignResourceState>();
if (resource instanceof IFolder && WGADesignStructureHelper.isDirlinkFolder((IFolder)resource)) {
// we have to resolve dirlink
IContainer dirlinkTarget = WGADesignStructureHelper.resolveDirLink(((IFolder)resource).getFile(WGUtils.DIRLINK_FILE));
if (dirlinkTarget != null && dirlinkTarget.isAccessible()) {
states.addAll(buildDirLinkFSDesignResourceStates(dirlinkTarget, resource.getName(), dirlinkTarget));
}
} else {
LocalFSDesignResourceState state = new LocalFSDesignResourceState();
states.add(state);
state.setFqPath(resource.getFullPath());
state.setPath(computeRelativPathFrom(resource, base).makeRelative().toString());
if (resource instanceof IContainer) {
state.setType(FSDesignResourceState.TYPE_FOLDER);
IContainer container = (IContainer) resource;
for (IResource child : container.members()) {
List<LocalFSDesignResourceState> childStates = buildFSDesignResourceStates(child, base);
states.addAll(childStates);
}
} else if (resource instanceof IFile) {
IFile file = (IFile) resource;
state.setType(FSDesignResourceState.TYPE_FILE);
state.setLastmodified(file.getLocalTimeStamp());
try {
state.setMd5sum(WGUtils.createMD5HEX(file.getContents()));
} catch (Exception e) {
WGADesignerPlugin.getDefault().logError("Unable to compute md5 sum of resource '" + file.getLocation().toString() + "'.", e);
}
}
}
return states;
}
private List<LocalFSDesignResourceState> buildDirLinkFSDesignResourceStates(IContainer linkTarget, String linkName, IResource resource) throws CoreException {
List<LocalFSDesignResourceState> states = new ArrayList<LocalFSDesignResourceState>();
LocalFSDesignResourceState state = new LocalFSDesignResourceState();
states.add(state);
state.setDirLinkName(linkName);
state.setDirLinkTarget(linkTarget);
state.setFqPath(resource.getFullPath());
state.setPath(new Path(linkName).append(computeRelativPathFrom(resource, linkTarget.getFullPath()).makeRelative()).toString());
if (resource instanceof IContainer) {
state.setType(FSDesignResourceState.TYPE_FOLDER);
IContainer container = (IContainer) resource;
for (IResource child : container.members()) {
List<LocalFSDesignResourceState> childStates = buildDirLinkFSDesignResourceStates(linkTarget, linkName, child);
states.addAll(childStates);
}
} else if (resource instanceof IFile) {
IFile file = (IFile) resource;
state.setType(FSDesignResourceState.TYPE_FILE);
state.setLastmodified(file.getLocalTimeStamp());
try {
state.setMd5sum(WGUtils.createMD5HEX(file.getContents()));
} catch (Exception e) {
WGADesignerPlugin.getDefault().logError("Unable to compute md5 sum of resource '" + file.getLocation().toString() + "'.", e);
}
}
return states;
}
private IPath computeRelativPathFromDesignRoot(IResource resource) {
int matchingSegments = resource.getFullPath().matchingFirstSegments(_runtime.getDesignRoot().getFullPath());
return resource.getFullPath().removeFirstSegments(matchingSegments).makeRelative();
}
private IPath computeRelativPathFrom(IResource resource, IPath base) {
int matchingSegments = resource.getFullPath().matchingFirstSegments(base);
return resource.getFullPath().removeFirstSegments(matchingSegments).makeRelative();
}
private IResource convertToResource(FSDesignResourceState state) {
// compute full qualified path to resource
IPath path = null;
if (state instanceof LocalFSDesignResourceState) {
// local resource use full qualified path
path = ((LocalFSDesignResourceState)state).getFqPath();
} else {
// remote state
// compute design folder name
String[] pathElements = state.getPath().split("/");
if (pathElements != null && pathElements.length > 0) {
String designFolderName = pathElements[0];
// check if this design is internal or external
IFolder designFolder = _runtime.getDesignRoot().getFolder(new Path(designFolderName));
if (WGADesignStructureHelper.isDirlinkFolder(designFolder)) {
// this is an external design
IContainer linkTarget = WGADesignStructureHelper.resolveDirLink(designFolder.getFile(WGUtils.DIRLINK_FILE));
path = linkTarget.getFullPath().append(new Path(state.getPath()).removeFirstSegments(1));
} else {
path = _runtime.getDesignRoot().getFullPath().append(new Path(state.getPath()));
}
} else {
path = _runtime.getDesignRoot().getFullPath().append(new Path(state.getPath()));
}
}
IResource resource = null;
if (state.getType() == FSDesignResourceState.TYPE_FOLDER) {
resource = ResourcesPlugin.getWorkspace().getRoot().getFolder(path);
} else {
resource = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
}
return resource;
}
public static IContainer retrieveVirtualPluginRoot(WGARuntime runtime) {
IProject project = runtime.getProject();
return project.getFolder("plugins");
}
public static IResource buildVirtualPluginResource(WGARuntime runtime, PluginInfo info) {
return retrieveVirtualPluginRoot(runtime).getFile(new Path(info.getUniqueName() + "-" + info.getVersion() + ".wgaplugin"));
}
}