Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 33fc6a3f169d17a517d88df1fb63f00df1afbefe (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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/*******************************************************************************
 * Copyright (c) 2011, 2012 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:
 * William Chen (Wind River)- [345387]Open the remote files with a proper editor
 *******************************************************************************/
package org.eclipse.tcf.te.tcf.filesystem.core.internal.url;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.atomic.AtomicReference;

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.protocol.Protocol;
import org.eclipse.tcf.services.IFileSystem;
import org.eclipse.tcf.services.IFileSystem.DoneClose;
import org.eclipse.tcf.services.IFileSystem.DoneOpen;
import org.eclipse.tcf.services.IFileSystem.FileSystemException;
import org.eclipse.tcf.services.IFileSystem.IFileHandle;
import org.eclipse.tcf.te.tcf.core.Tcf;
import org.eclipse.tcf.te.tcf.filesystem.core.internal.exceptions.TCFChannelException;
import org.eclipse.tcf.te.tcf.filesystem.core.internal.operations.Operation;
import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerModel;
import org.eclipse.tcf.te.tcf.locator.interfaces.services.ILocatorModelLookupService;
import org.eclipse.tcf.te.tcf.locator.model.Model;

/**
 * The URL connection returned by TCF stream service used to handle "tcf"
 * stream protocol.
 */
public class TcfURLConnection extends URLConnection {
	// Default connecting timeout.
	private static final int DEFAULT_CONNECT_TIMEOUT = 5000;
	// Default file opening timeout.
	private static final int DEFAULT_OPEN_TIMEOUT = 5000;
	// Default file reading timeout.
	private static final int DEFAULT_READ_TIMEOUT = 5000;
	// Default file closing timeout.
	private static final int DEFAULT_CLOSE_TIMEOUT = 5000;
	// The schema name of the stream protocol.
	public static final String PROTOCOL_SCHEMA = "tcf"; //$NON-NLS-1$

	// The input stream of this connection.
	private TcfInputStream inputStream;
	// The output stream of this connection.
	private TcfOutputStream outputStream;

	// The TCF agent peer of the connection.
	private IPeer peer;
	// The path to the resource on the remote file system.
	String path;
	// The timeout for opening a file.
	private int openTimeout;
	// The timeout for closing a file.
	private int closeTimeout;

	// The TCF channel used to open and read the resource.
	IChannel channel;
	// The file's handle
	IFileHandle handle;
	// The file service
	IFileSystem service;

	/**
	 * Create a TCF URL Connection using the specified url. The format of this
	 * URL should be: tcf:/<TCF_AGENT_ID>/remote/path/to/the/resource... The
	 * stream protocol schema is designed in this way in order to retrieve the
	 * agent peer ID without knowing the structure of a TCF peer id.
	 *
	 * @see TcfURLStreamHandlerService#parseURL(URL, String, int, int)
	 * @param url
	 *            The URL of the resource.
	 */
	public TcfURLConnection(final URL url) {
		super(url);
		// The peerId is stored as the host name in URL. See TcfURLStreamHandlerService#parseURL for details.
		String peerId = url.getHost();
		Assert.isNotNull(peerId);
		peer = findPeer(peerId);
		if(peer == null) {
			throw new IllegalArgumentException(NLS.bind(Messages.TcfURLConnection_NoPeerFound, peerId));
		}
		path = url.getPath();
		// Set default timeout.
		setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
		setOpenTimeout(DEFAULT_OPEN_TIMEOUT);
		setReadTimeout(DEFAULT_READ_TIMEOUT);
		setCloseTimeout(DEFAULT_CLOSE_TIMEOUT);
	}

	/**
	 * Find the TCF peer with the specified ID.
	 *
	 * @param peerId The target peer's ID.
	 * @return The peer with this ID or null if not found.
	 */
    private IPeer findPeer(final String peerId) {
    	Assert.isNotNull(peerId);

    	final AtomicReference<IPeer> peer = new AtomicReference<IPeer>();

    	Runnable runnable = new Runnable() {

			@Override
			public void run() {
				IPeer p = Protocol.getLocator().getPeers().get(peerId);
				if (p == null) {
					IPeerModel peerNode = Model.getModel().getService(ILocatorModelLookupService.class).lkupPeerModelById(peerId);
					if (peerNode != null) p = peerNode.getPeer();
				}
				peer.set(p);
			}
		};

		if (Protocol.isDispatchThread()) runnable.run();
		else Protocol.invokeAndWait(runnable);

		return peer.get();
	}

	/**
	 * Get the timeout for closing a file.
	 *
	 * @return the timeout in milliseconds.
	 */
	public long getCloseTimeout() {
		return closeTimeout;
	}

	/**
	 * Set the timeout for closing a file.
	 *
	 * @param closeTimeout
	 *            the timeout in milliseconds.
	 */
	public void setCloseTimeout(int closeTimeout) {
		this.closeTimeout = closeTimeout;
	}

	/**
	 * Get the timeout for opening a file.
	 *
	 * @return the timeout in milliseconds.
	 */
	public long getOpenTimeout() {
		return openTimeout;
	}

	/**
	 * Set the timeout for opening a file.
	 *
	 * @param openTimeout
	 *            the timeout in milliseconds.
	 */
	public void setOpenTimeout(int openTimeout) {
		this.openTimeout = openTimeout;
	}

	/**
	 * Open a file on the remote file system for read/write and store the file handle.
	 *
	 * @throws IOException Opening file fails.
	 */
	private void openFile() throws IOException {
		if(peer == null)
			throw new IOException(Messages.TcfURLConnection_NoSuchTcfAgent);
		try {
			// Open the channel
			channel = Operation.openChannel(peer);
		} catch (TCFChannelException e) {
			throw new IOException(e.getMessage());
		}
		if (channel != null) {
			service = Operation.getBlockingFileSystem(channel);
			if (service != null) {
				final FileSystemException[] errors = new FileSystemException[1];
				// Open the file.
				int open_flag = 0;
				if (doInput)
					open_flag |= IFileSystem.TCF_O_READ;
				if (doOutput)
					open_flag |= IFileSystem.TCF_O_WRITE | IFileSystem.TCF_O_CREAT | IFileSystem.TCF_O_TRUNC;
				service.open(path, open_flag, null, new DoneOpen() {
					@Override
					public void doneOpen(IToken token, FileSystemException error, IFileHandle hdl) {
						errors[0] = error;
						handle = hdl;
					}
				});
				if (errors[0] != null) {
					IOException exception = new IOException(errors[0].toString());
					exception.initCause(errors[0]);
					throw exception;
				}
				if (handle == null) {
					throw new IOException(Messages.TcfURLConnection_NoFileHandleReturned);
				}
			} else {
				throw new IOException(Messages.Operation_NoFileSystemError);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see java.net.URLConnection#connect()
	 */
	@Override
	public void connect() throws IOException {
		if (!connected) {
			openFile();
			if (doInput) {
				inputStream = new TcfInputStream(this);
			}
			if (doOutput) {
				outputStream = new TcfOutputStream(this);
			}
			connected = true;
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see java.net.URLConnection#getInputStream()
	 */
	@Override
	public InputStream getInputStream() throws IOException {
		if (!connected)
			connect();
		return inputStream;
	}

	/*
	 * (non-Javadoc)
	 * @see java.net.URLConnection#getOutputStream()
	 */
	@Override
	public OutputStream getOutputStream() throws IOException {
		if (!connected)
			connect();
		return outputStream;
	}

	/**
	 * Close the stream, release its file handler and close
	 * the TCF channel used if possible.
	 *
	 * @param stream The stream either the input stream or the output stream.
	 * @throws IOException If closing file handle times out.
	 */
	public synchronized void closeStream(Closeable stream) throws IOException {
		boolean shouldClose = shouldCloseFileHandle(stream);
		if (shouldClose) {
			service.close(handle, new DoneClose() {
				@Override
				public void doneClose(IToken token, FileSystemException error) {
					Tcf.getChannelManager().closeChannel(channel);
				}
			});
		}
	}

	/**
	 * Decide if the file handle and the TCF channel should be closed if
	 * the specified stream is closed. If the stream is the last stream
	 * that depends on the file handle and the TCF channel, then it should
	 * be closed.
	 *
	 * @param stream The stream to be closed.
	 * @return true if the file handle and the TCF channel should be closed.
	 */
	private boolean shouldCloseFileHandle(Closeable stream) {
		boolean shouldClose = false;
		if (stream == inputStream) {
			if (doOutput) {
				if (outputStream.closed) {
					shouldClose = true;
				}
			} else {
				shouldClose = true;
			}
		} else if (stream == outputStream) {
			if (doInput) {
				if (inputStream.closed)
					shouldClose = true;
			} else {
				shouldClose = true;
			}
		}
		return shouldClose;
	}
}

Back to the top