/** * This file Copyright (c) 2005-2007 Aptana, Inc. This program is * dual-licensed under both the Aptana Public License and the GNU General * Public license. You may elect to use one or the other of these licenses. * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by whichever of * the GPL or APL you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or modify this * program under the terms of the GNU General Public License, * Version 3, as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain Eclipse Public Licensed code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * 2. For the Aptana Public License (APL), this program and the * accompanying materials are made available under the terms of the APL * v1.0 which accompanies this distribution, and is available at * http://www.aptana.com/legal/apl/. * * You may view the GPL, Aptana's exception and additional terms, and the * APL in the file titled license.html at the root of the corresponding * plugin containing this source file. * * Any modifications to this file must keep this entire header intact. */ package com.aptana.ide.syncing.ui; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import com.aptana.ide.core.FileUtils; import com.aptana.ide.core.ILoggable; import com.aptana.ide.core.ILogger; import com.aptana.ide.core.StringUtils; import com.aptana.ide.core.io.IConnectionPoint; import com.aptana.ide.core.io.efs.EFSUtils; import com.aptana.ide.core.io.syncing.SyncState; import com.aptana.ide.core.io.syncing.VirtualFileSyncPair; import com.aptana.ide.core.model.BaseModelObject; import com.aptana.ide.syncing.core.Synchronizer; /** * @author Kevin Sawicki (ksawicki@aptana.com) */ public class SyncModel extends BaseModelObject implements ILoggable { /** * USE_STREAM */ public static final int USE_STREAM = -100; /** * WAITING */ public static final int WAITING = 0; /** * RUNNING */ public static final int RUNNING = 1; /** * SUCCESS */ public static final int SUCCESS = 2; /** * FAILURE */ public static final int FAILURE = 3; /** * Sync Pair */ public class SyncPair extends VirtualFileSyncPair { int status; IConnectionPoint sourceManager; IConnectionPoint destManager; String fromEndpoint; String fromFolder; String toEndpoint; String toFolder; SyncPair(VirtualFileSyncPair vfsPair) { this(vfsPair.getSourceFile(), vfsPair.getDestinationFile(), vfsPair.getRelativePath(), vfsPair .getSyncState()); } SyncPair(IFileStore sourceFile, IFileStore destinationFile, String relativePath, int syncState) { super(sourceFile, destinationFile, relativePath, syncState); } /** * Gets the destination manager * * @return - vfm */ public IConnectionPoint getDestManager() { return destManager; } /** * Gets the source manager * * @return - vfm */ public IConnectionPoint getSourceManager() { return sourceManager; } /** * @return the status */ public int getStatus() { return status; } /** * @return the fromEndpoint */ public String getFromEndpoint() { return fromEndpoint; } /** * @return the fromFolder */ public String getFromFolder() { return fromFolder; } /** * @return the toEndpoint */ public String getToEndpoint() { return toEndpoint; } /** * @return the toFolder */ public String getToFolder() { return toFolder; } } private class Pair { Object from; Object to; Pair(Object from, Object to) { this.from = from; this.to = to; } } private List<SyncPair> items; private List<Pair> toBeProcessed; private SyncPair lastSync; private SyncPair lastAdded; private SyncPair currentSync; private Job buildJob; private Job syncJob; private ILogger logger; /** * Sync model */ public SyncModel() { this.items = new ArrayList<SyncPair>(); this.toBeProcessed = new ArrayList<Pair>(); this.lastSync = null; this.lastAdded = null; this.currentSync = null; listeners = new ListenerList(); buildJob = new Job("Building sync pairs") //$NON-NLS-1$ { protected IStatus run(IProgressMonitor monitor) { Pair pair = null; synchronized (toBeProcessed) { while (toBeProcessed.isEmpty()) { try { toBeProcessed.wait(); } catch (InterruptedException e) { } if (monitor != null && monitor.isCanceled()) { return Status.CANCEL_STATUS; } } if (toBeProcessed.size() > 0) { pair = toBeProcessed.remove(0); } } if (pair != null) { String fromEnd = null; String fromFolder = null; String toEnd = null; String toFolder = null; Object from = pair.from; Object to = pair.to; IConnectionPoint fromPoint = null; IConnectionPoint toPoint = null; IFileStore fromFile = null; IFileStore toFile = null; // if (from instanceof IResource) // { // IResource resource = (IResource) from; // fromEnd = resource.getProject().getName(); // fromFolder = resource.getProjectRelativePath().toString(); // fromFile = new LocalFile(resource.getLocation();ProjectFileManager.convertResourceToFile(from); // } //else if (from instanceof IFileStore) { fromFile = (IFileStore) from; fromEnd = fromPoint.getName(); fromFolder = EFSUtils.getRelativePath(fromPoint, fromFile); } // if (to instanceof IResource) // { // IResource resource = (IResource) to; // toEnd = resource.getProject().getName(); // toFolder = resource.getProjectRelativePath().toString(); // toFile = ProjectFileManager.convertResourceToFile(to); // } //else if (to instanceof IFileStore) { toFile = (IFileStore) to; toEnd = toPoint.getName(); toFolder = EFSUtils.getRelativePath(toPoint, toFile); } else if (to instanceof IConnectionPoint) { try { toFile = ((IConnectionPoint) to).getRoot(); } catch (CoreException e) { // TODO Auto-generated catch block e.printStackTrace(); } toEnd = ((IConnectionPoint) to).getName(); toFolder = EFSUtils.getRelativePath(toPoint, toFile); } if (fromFile != null && toFile != null) { if (fromFile.fetchInfo().isDirectory()) { try { boolean dirExists = false; IFileStore toDir = toFile.getFileStore(new Path(fromFile.getName())); // getFileManager().createVirtualDirectory( // EFSUtils.getAbsolutePath(toFile) + toFile.getFileManager().getFileSeparator() // + fromFile.getName()); IFileStore[] files = EFSUtils.getFiles(toFile, null); for (IFileStore target : files) { if (target.fetchInfo().isDirectory() && target.getName().equals(fromFile.getName())) { dirExists = true; break; } } if (!dirExists) { toDir.mkdir(EFS.NONE, null); //toFile.getFileManager().createLocalDirectory(toDir); if (to instanceof IContainer) { try { ((IContainer) to).refreshLocal(IResource.DEPTH_INFINITE, null); } catch (CoreException e) { } } } IConnectionPoint fromManager = (IConnectionPoint)fromPoint; //fromFile.getFileManager().cloneManager(); IConnectionPoint toManager = (IConnectionPoint)toPoint; //toFile.getFileManager().cloneManager(); if (fromManager != null && toManager != null) { //fromManager.(EFSUtils.getAbsolutePath(fromFile)); //fromFile = fromManager.createVirtualDirectory(EFSUtils.getAbsolutePath(fromFile)); //toManager.setBasePath(EFSUtils.getAbsolutePath(toDir)); //toDir = toManager.createVirtualDirectory(EFSUtils.getAbsolutePath(toDir)); Synchronizer syncer = new Synchronizer(); syncer.setLogger(logger); syncer.setServerFileManager(toManager); syncer.setClientFileManager(fromManager); VirtualFileSyncPair[] pairs = syncer.getSyncItems(fromPoint, toPoint, fromFile, toDir, monitor); for (VirtualFileSyncPair vfsPair : pairs) { if (vfsPair.getSyncState() != SyncState.ClientItemOnly) { vfsPair.setSyncState(SyncState.ClientItemIsNewer); } SyncPair syncPair = new SyncPair(vfsPair); syncPair.fromEndpoint = fromEnd; syncPair.fromFolder = fromFolder; syncPair.toEndpoint = toEnd; syncPair.toFolder = toFolder; syncPair.status = SyncModel.WAITING; syncPair.destManager = toManager; syncPair.sourceManager = fromManager; synchronized (items) { items.add(syncPair); lastAdded = syncPair; fireChange(); items.notify(); } } } } catch (IOException e) { log(StringUtils.format(Messages.Synchronizer_Error, e.getLocalizedMessage())); } catch (CoreException e) { log(StringUtils.format(Messages.Synchronizer_Error, e.getLocalizedMessage())); } } else { toFile = toFile.getFileStore(new Path(fromFile.getName())); // getFileManager().createVirtualFile( // EFSUtils.getAbsolutePath(toFile) + toFile.getFileManager().getFileSeparator() // + fromFile.getName()); VirtualFileSyncPair vfsPair = new VirtualFileSyncPair(fromFile, toFile, "", USE_STREAM); //$NON-NLS-1$ SyncPair syncPair = new SyncPair(vfsPair); syncPair.fromEndpoint = fromEnd; syncPair.fromFolder = fromFolder; syncPair.toEndpoint = toEnd; syncPair.toFolder = toFolder; syncPair.status = SyncModel.WAITING; syncPair.destManager = toPoint; syncPair.sourceManager = fromPoint; synchronized (items) { items.add(syncPair); lastAdded = syncPair; fireChange(); items.notify(); } } } } if (monitor == null || !monitor.isCanceled()) { this.schedule(); } return Status.OK_STATUS; } }; buildJob.setPriority(Job.BUILD); buildJob.setSystem(true); buildJob.schedule(); syncJob = new Job("Running Sync jobs") //$NON-NLS-1$ { protected IStatus run(IProgressMonitor monitor) { synchronized (items) { while (items.isEmpty()) { try { items.wait(); } catch (InterruptedException e) { } if (monitor != null && monitor.isCanceled()) { return Status.CANCEL_STATUS; } } if (items.size() > 0) { SyncPair pair = items.remove(0); currentSync = pair; currentSync.status = SyncModel.RUNNING; fireChange(); if (pair.getSyncState() == USE_STREAM) { try { log(FileUtils.NEW_LINE + StringUtils.format(Messages.Synchronizer_Uploading, EFSUtils.getAbsolutePath(pair.getSourceFile()))); if (!pair.getDestinationFile().equals(pair.getSourceFile())) { pair.getSourceFile().copy(pair.getDestinationFile(), EFS.OVERWRITE, null); //pair.getDestinationFile().putStream(pair.getSourceFile().openInputStream(EFS.NONE, null)); try { EFSUtils.setModificationTime(pair.getSourceFileInfo().getLastModified(), pair.getDestinationFile()); } catch (Exception e) { // Catch exceptions here since although the file was uploaded, not being able to // set the mod time shouldn't trigger an error } } log(Messages.Synchronizer_Success); pair.status = SyncModel.SUCCESS; lastSync = pair; fireChange(); } catch (Exception e) { log(StringUtils.format(Messages.Synchronizer_Error, e.getLocalizedMessage())); pair.status = SyncModel.FAILURE; lastSync = pair; fireChange(); } } else { Synchronizer syncer = new Synchronizer(); syncer.setLogger(logger); try { syncer.setClientFileManager(pair.sourceManager); syncer.setServerFileManager(pair.destManager); syncer.uploadAndDelete(new VirtualFileSyncPair[] { pair }, null); pair.status = SyncModel.SUCCESS; lastSync = pair; fireChange(); } catch (Exception e) { log(StringUtils.format(Messages.Synchronizer_Error, e.getLocalizedMessage())); pair.status = SyncModel.FAILURE; lastSync = pair; fireChange(); } } } } if (monitor == null || !monitor.isCanceled()) { this.schedule(); } return Status.OK_STATUS; } }; syncJob.setPriority(Job.BUILD); syncJob.setSystem(true); syncJob.schedule(); } /** * Disposes the model */ public void dispose() { synchronized (items) { syncJob.cancel(); items.notify(); } synchronized (toBeProcessed) { buildJob.cancel(); toBeProcessed.notify(); } } private synchronized void log(String message) { if (this.logger != null) { this.logger.logInfo(message); } } /** * Adds items to be synced * * @param from * @param to */ public void addSyncing(Object from, Object to) { synchronized (toBeProcessed) { toBeProcessed.add(new Pair(from, to)); toBeProcessed.notify(); } } /** * Gets the items in the queue * * @return - array of items */ public SyncPair[] getItems() { return this.items.toArray(new SyncPair[0]); } /** * @return the lastSync */ public SyncPair getLastSync() { return this.lastSync; } /** * @return the lastSync */ public SyncPair getLastAdded() { return this.lastAdded; } /** * @return the currentSync */ public SyncPair getCurrentSync() { return this.currentSync; } /** * @see com.aptana.ide.core.ILoggable#getLogger() */ public ILogger getLogger() { return this.logger; } /** * @see com.aptana.ide.core.ILoggable#setLogger(com.aptana.ide.core.ILogger) */ public synchronized void setLogger(ILogger logger) { this.logger = logger; } }