/******************************************************************************* * Copyright (c) 2009 Remy Chi Jian Suen and others. * 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: * Remy Chi Jian Suen <remy.suen@gmail.com> - initial API and implementation ******************************************************************************/ package org.eclipse.ecf.internal.sync.resources.core; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.MultiRule; import org.eclipse.ecf.core.identity.ID; import org.eclipse.ecf.core.util.ECFException; import org.eclipse.ecf.datashare.AbstractShare; import org.eclipse.ecf.datashare.IChannelContainerAdapter; import org.eclipse.ecf.sync.IModelChange; import org.eclipse.ecf.sync.IModelChangeMessage; public class ResourcesShare extends AbstractShare { private Boolean response; private Set sharedProjects = new HashSet(); private ID receiverID; private ID containerID; private ID localID; public ResourcesShare(ID containerID, IChannelContainerAdapter adapter) throws ECFException { super(adapter); this.containerID = containerID; } private void attachListener() { if (sharedProjects.size() == 1) { SyncResourcesCore.getDefault().attachListener(); } } private void detachListener() { if (sharedProjects.isEmpty()) { SyncResourcesCore.getDefault().detachListener(); } } public void sendResponse(boolean accept, String projectName) { try { if (accept) { send(receiverID, new AcceptMessage(projectName)); } else { send(receiverID, new DenyMessage(projectName)); } } catch (ECFException e) { // TODO handle this e.printStackTrace(); } } public Boolean getResponse() { if (response == null) { return null; } Boolean temp = response; response = null; return temp; } public ID getContainerID() { return containerID; } public ID getReceiverID() { return receiverID; } public ID getLocalID() { return localID; } public boolean isSharing(String projectName) { synchronized (sharedProjects) { return sharedProjects.contains(projectName); } } public void startShare(ID fromId, ID toID, String projectName) throws ECFException { if (sharedProjects.add(projectName)) { // reset in case we have a stale one response = null; try { send(toID, new StartMessage(projectName, fromId, toID)); localID = fromId; receiverID = toID; attachListener(); } catch (ECFException e) { receiverID = null; sharedProjects.remove(projectName); detachListener(); throw e; } } } public void stopSharing(String projectName) { if (sharedProjects.remove(projectName)) { try { send(receiverID, new StopMessage(projectName)); } catch (ECFException e) { // TODO handle this e.printStackTrace(); } finally { receiverID = null; } detachListener(); } } private void send(ID toID, Message message) throws ECFException { sendMessage(toID, message.serialize()); } void send(byte[] bytes) throws ECFException { sendMessage(receiverID, bytes); } void sendResourceChangeMessage(IResource resource, int kind) { try { IModelChange change = ResourceChangeMessage.createResourceChange( resource, kind); IModelChangeMessage[] messages = ResourcesSynchronizationStrategy .getInstance().registerLocalChange(change); for (int i = 0; i < messages.length; i++) { send(messages[i].serialize()); } } catch (ECFException e) { // TODO Auto-generated catch block e.printStackTrace(); } } protected void handleStartMessage(StartMessage msg) { receiverID = msg.getFromId(); localID = msg.getLocalId(); sharedProjects.add(msg.getProjectName()); attachListener(); } private void handleStopMessage(StopMessage msg) { sharedProjects.remove(msg.getProjectName()); detachListener(); } private void handleResourceChangeMessage(byte[] data) throws Exception { IModelChange remoteChange = ResourcesSynchronizationStrategy .getInstance().deserializeRemoteChange(data); final IModelChange[] remoteChanges = ResourcesSynchronizationStrategy .getInstance().transformRemoteChange(remoteChange); // create a scheduling rule to lock the projects ISchedulingRule[] rules = new ISchedulingRule[sharedProjects.size()]; int index = 0; for (Iterator it = sharedProjects.iterator(); it.hasNext();) { String projectName = (String) it.next(); rules[index] = ResourcesPlugin.getWorkspace().getRoot().getProject( projectName); index++; } try { // lock to prevent resource changes from being propagated lock(remoteChanges); applyRemoteChanges(remoteChanges, new MultiRule(rules)); } finally { // unlock now that we've applied the remote changes to our // own workspace unlock(remoteChanges); } if (remoteChange instanceof BatchModelChange) { BatchModelChange batchChange = (BatchModelChange) remoteChange; batchChange.setOutgoing(false); batchChange.setTime(System.currentTimeMillis()); SyncResourcesCore.add(batchChange); } } protected void lock(IModelChange[] remoteChanges) { SyncResourcesCore.lock(); } protected void unlock(IModelChange[] remoteChanges) { SyncResourcesCore.unlock(); } private void applyRemoteChanges(final IModelChange[] remoteChanges, ISchedulingRule rule) throws CoreException { ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { monitor.beginTask("Processing remote changes...", remoteChanges.length); for (int i = 0; i < remoteChanges.length; i++) { if (monitor.isCanceled()) { return; } // applies the resource changes remoteChanges[i].applyToModel(containerID); monitor.worked(1); } monitor.done(); } }, rule, IWorkspace.AVOID_UPDATE, null); } protected void handleMessage(ID fromContainerID, byte[] data) { try { Object message = Message.deserialize(data); if (message instanceof StartMessage) { handleStartMessage((StartMessage) message); } else if (message instanceof StopMessage) { handleStopMessage((StopMessage) message); } else if (message instanceof AcceptMessage) { response = Boolean.TRUE; } else if (message instanceof DenyMessage) { sharedProjects.remove(((DenyMessage) message).getProjectName()); receiverID = null; detachListener(); response = Boolean.FALSE; } else { handleResourceChangeMessage(data); } } catch (Exception e) { e.printStackTrace(); } } }