Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: ede34d842a0a8839c0c77adc2532a58bff3d7b36 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/*******************************************************************************
 * 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
 * William Chen (Wind River) - [361324] Add more file operations in the file system
 * 												of Target Explorer.
 *******************************************************************************/
package org.eclipse.tcf.te.tcf.filesystem.internal.handlers;

import java.io.File;

import org.eclipse.core.runtime.Assert;
import org.eclipse.osgi.util.NLS;
import org.eclipse.tcf.protocol.IChannel;
import org.eclipse.tcf.protocol.IPeer;
import org.eclipse.tcf.protocol.IToken;
import org.eclipse.tcf.services.IFileSystem;
import org.eclipse.tcf.services.IFileSystem.DoneSetStat;
import org.eclipse.tcf.services.IFileSystem.DoneStat;
import org.eclipse.tcf.services.IFileSystem.FileAttrs;
import org.eclipse.tcf.services.IFileSystem.FileSystemException;
import org.eclipse.tcf.te.tcf.filesystem.internal.exceptions.TCFChannelException;
import org.eclipse.tcf.te.tcf.filesystem.internal.exceptions.TCFException;
import org.eclipse.tcf.te.tcf.filesystem.internal.exceptions.TCFFileSystemException;
import org.eclipse.tcf.te.tcf.filesystem.internal.nls.Messages;
import org.eclipse.tcf.te.tcf.filesystem.internal.url.Rendezvous;
import org.eclipse.tcf.te.tcf.filesystem.model.CacheState;
import org.eclipse.tcf.te.tcf.filesystem.model.FSModel;
import org.eclipse.tcf.te.tcf.filesystem.model.FSTreeNode;
import org.eclipse.tcf.te.tcf.core.Tcf;
import org.eclipse.tcf.te.tcf.core.interfaces.IChannelManager.DoneOpenChannel;

/**
 * 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);
	}

	/**
	 * 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.OpeningChannelFailureMessage,
							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.OpeningChannelFailureMessage,
					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;
	}
}

Back to the top