/******************************************************************************* * Copyright (c) 2011 Wind River Systems, Inc. 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: * Wind River Systems - initial API and implementation * William Chen (Wind River)- [345552] Edit the remote files with a proper editor *******************************************************************************/ package org.eclipse.tm.te.tcf.filesystem.internal.handlers; import java.io.File; import org.eclipse.core.runtime.Assert; import org.eclipse.osgi.util.NLS; import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IPeer; import org.eclipse.tm.tcf.protocol.IToken; import org.eclipse.tm.tcf.services.IFileSystem; import org.eclipse.tm.tcf.services.IFileSystem.DoneSetStat; import org.eclipse.tm.tcf.services.IFileSystem.DoneStat; import org.eclipse.tm.tcf.services.IFileSystem.FileAttrs; import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException; import org.eclipse.tm.te.tcf.core.Tcf; import org.eclipse.tm.te.tcf.core.interfaces.IChannelManager.DoneOpenChannel; import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFChannelException; import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFException; import org.eclipse.tm.te.tcf.filesystem.internal.exceptions.TCFFileSystemException; import org.eclipse.tm.te.tcf.filesystem.internal.nls.Messages; import org.eclipse.tm.te.tcf.filesystem.internal.url.Rendezvous; import org.eclipse.tm.te.tcf.filesystem.model.CacheState; import org.eclipse.tm.te.tcf.filesystem.model.FSModel; import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode; /** * This class provides several utility methods to get, update, commit * or refresh a file node's state. * */ public class StateManager { // The singleton instance. private static StateManager instance; /** * Get the singleton user manager. * * @return The singleton cache manager. */ public static StateManager getInstance() { if (instance == null) { instance = new StateManager(); } return instance; } /** * Create a StateManager fInstance. */ private StateManager() { } /** * Update the state of the specified node. * * @param node The tree node whose state is going to be updated. * @throws TCFException */ public void updateState(FSTreeNode node) throws TCFException { updateFileStat(node, true); } /** * Refresh the state of the specified node. * * @param node The tree node whose state is going to be refreshed. * @throws TCFException */ public void refreshState(FSTreeNode node) throws TCFException { updateFileStat(node, false); } /** * Update the file's state of the specified tree node. Synchronize the time stamp of * the file with the base time stamp and that of the remote file if sync is true. * * @param node The tree node whose file state is going to be updated. * @param sync If its base time stamp and its remote file's time stamp should be synchronized. */ private void updateFileStat(final FSTreeNode node, final boolean sync) throws TCFException { IChannel channel = null; try { channel = openChannel(node.peerNode.getPeer()); if (channel != null) { IFileSystem service = channel.getRemoteService(IFileSystem.class); if (service != null) { final TCFFileSystemException[] errors = new TCFFileSystemException[1]; final Rendezvous rendezvous = new Rendezvous(); String path = node.getLocation(true); service.stat(path, new DoneStat() { @Override public void doneStat(IToken token, FileSystemException error, FileAttrs attrs) { if (error == null) { updateNodeAttr(node, attrs, sync); } else { String message = NLS.bind(Messages.StateManager_CannotGetFileStatMessage, new Object[]{node.name, error}); errors[0] = new TCFFileSystemException(message, error); } rendezvous.arrive(); } }); try { rendezvous.waiting(5000L); } catch (InterruptedException e) { String message = NLS.bind(Messages.StateManager_CannotGetFileStateMessage2, new Object[]{node.name, e}); errors[0] = new TCFFileSystemException(message, e); } if (errors[0] != null) { throw errors[0]; } }else{ String message = NLS.bind(Messages.StateManager_TCFNotProvideFSMessage, node.peerNode.getPeer().getID()); throw new TCFFileSystemException(message); } } } finally { if (channel != null) channel.close(); } } /** * Update the file attribute of the specified tree node with the specified value * and synchronize its base timestamp and its remote file's timestamp if "sync" is true. * * @param node The tree node whose file attribute is to updated. * @param attr The new file attribute. * @param sync If the timestamps should be synchronized. */ void updateNodeAttr(FSTreeNode node, FileAttrs attr, boolean sync){ node.attr = attr; if (sync) { File file = CacheManager.getInstance().getCacheFile(node); Assert.isTrue(file.exists()); file.setLastModified(attr.mtime); PersistenceManager.getInstance().setBaseTimestamp(node.getLocationURL(), attr.mtime); } FSModel.getInstance().fireNodeStateChanged(node); } /** * Commit the content of the local file to the target. * * @param node The tree node whose local file is going to committed. * @throws TCFException */ public void commitState(final FSTreeNode node) throws TCFException { File file = CacheManager.getInstance().getCacheFile(node); Assert.isTrue(file.exists()); long mtime = file.lastModified(); // Create the new file attribute based on the file's last modified time. IFileSystem.FileAttrs attrs = new IFileSystem.FileAttrs(node.attr.flags, node.attr.size, node.attr.uid, node.attr.gid, node.attr.permissions, node.attr.atime, mtime, node.attr.attributes); setFileAttrs(node, attrs); } /** * Set the file's attributes using the new attributes. * * @param node The file's node. * @param attrs The new file attributes. * @throws TCFException */ public void setFileAttrs(final FSTreeNode node, final IFileSystem.FileAttrs attrs) throws TCFException { IChannel channel = null; try { channel = openChannel(node.peerNode.getPeer()); if (channel != null) { IFileSystem service = channel.getRemoteService(IFileSystem.class); if (service != null) { final TCFFileSystemException[] errors = new TCFFileSystemException[1]; final Rendezvous rendezvous = new Rendezvous(); String path = node.getLocation(true); service.setstat(path, attrs, new DoneSetStat() { @Override public void doneSetStat(IToken token, FileSystemException error) { if (error == null) { commitNodeAttr(node, attrs); } else { String message = NLS.bind(Messages.StateManager_CannotSetFileStateMessage, new Object[] { node.name, error }); errors[0] = new TCFFileSystemException(message, error); } rendezvous.arrive(); } }); try { rendezvous.waiting(5000L); } catch (InterruptedException e) { String message = NLS.bind(Messages.StateManager_CannotSetFileStateMessage2, new Object[] { node.name, e }); errors[0] = new TCFFileSystemException(message, e); } if (errors[0] != null) { throw errors[0]; } } else { String message = NLS.bind(Messages.StateManager_TCFNotProvideFSMessage2, node.peerNode.getPeer().getID()); throw new TCFFileSystemException(message); } } } finally { if (channel != null) channel.close(); } } /** * Open a channel connected to the target represented by the peer. * * @return The channel or null if the operation fails. */ private IChannel openChannel(final IPeer peer) throws TCFChannelException { final Rendezvous rendezvous = new Rendezvous(); final TCFChannelException[] errors = new TCFChannelException[1]; final IChannel[] channels = new IChannel[1]; Tcf.getChannelManager().openChannel(peer, new DoneOpenChannel(){ @Override public void doneOpenChannel(Throwable error, IChannel channel) { if(error!=null){ String message = NLS.bind(Messages.TCFUtilities_OpeningFailureMessage, new Object[]{peer.getID(), error.getLocalizedMessage()}); errors[0] = new TCFChannelException(message, error); }else{ channels[0] = channel; } rendezvous.arrive(); }}); try { rendezvous.waiting(5000L); } catch (InterruptedException e) { String message = NLS.bind(Messages.TCFUtilities_OpeningFailureMessage, new Object[]{peer.getID(), e.getLocalizedMessage()}); errors[0] = new TCFChannelException(message, e); } if(errors[0] != null){ throw errors[0]; } return channels[0]; } /** * Commit the file attribute of the specified tree node with the specified value. * * @param node The tree node whose file attribute is to committed. * @param attr The new file attribute. */ void commitNodeAttr(FSTreeNode node, FileAttrs attr){ node.attr = attr; PersistenceManager.getInstance().setBaseTimestamp(node.getLocationURL(), attr.mtime); FSModel.getInstance().fireNodeStateChanged(node); } /** * Get the local file's state of the specified tree node. The local file must exist * before calling this method to get its state. * * @param node The tree node whose local file state is going to retrieved. * @return The tree node's latest cache state. */ public CacheState getCacheState(FSTreeNode node) { File file = CacheManager.getInstance().getCacheFile(node); if(!file.exists()) return CacheState.consistent; long ltime = file.lastModified(); long btime = PersistenceManager.getInstance().getBaseTimestamp(node.getLocationURL()); long mtime = 0; if(node.attr!=null) mtime = node.attr.mtime; if(btime == ltime){ if(isUnchanged(mtime, btime)) return CacheState.consistent; return CacheState.outdated; } if(isUnchanged(mtime, btime)) return CacheState.modified; return CacheState.conflict; } /** * Compare the modified time of the remote file and the base timestamp * and see if they are equal to each other. * * @param mtime The modified time of the remote file. * @param btime The base timestamp cached. * @return true if they are equal in second precision. */ private boolean isUnchanged(long mtime, long btime){ return Math.abs(mtime-btime)/1000 == 0; } }