/******************************************************************************* * 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: * William Chen (Wind River)- [345387]Open the remote files with a proper editor *******************************************************************************/ package org.eclipse.tm.te.tcf.filesystem.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 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.DoneClose; import org.eclipse.tm.tcf.services.IFileSystem.DoneOpen; import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException; import org.eclipse.tm.tcf.services.IFileSystem.IFileHandle; 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.nls.Messages; import org.eclipse.tm.te.tcf.filesystem.model.FSModel; import org.eclipse.tm.te.tcf.filesystem.model.FSTreeNode; /** * The URL connection returned by TCF stream service used to handler "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. private 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; /** * 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. * * @param url * The URL of the resource. */ public TcfURLConnection(URL url) { super(url); // The path should have already contained the peer's id like: // /<TCF_AGENT_ID>/remote/path/to/the/resource... path = url.getPath(); int slash = path.indexOf("/", 1); //$NON-NLS-1$ if (slash != -1){ path = path.substring(slash); if (path.matches("/[A-Za-z]:.*")) path = path.substring(1); //$NON-NLS-1$ } //Get the peer using the peer id. FSTreeNode node = FSModel.getInstance().getTreeNode(url); if(node != null) peer = node.peerNode.getPeer(); // Set default timeout. setConnectTimeout(DEFAULT_CONNECT_TIMEOUT); setOpenTimeout(DEFAULT_OPEN_TIMEOUT); setReadTimeout(DEFAULT_READ_TIMEOUT); setCloseTimeout(DEFAULT_CLOSE_TIMEOUT); } /** * 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 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]; } /** * 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 = openChannel(peer); } catch (TCFChannelException e) { throw new IOException(e.getLocalizedMessage()); } if (channel != null) { IFileSystem service = channel.getRemoteService(IFileSystem.class); if (service != null) { final Rendezvous rendezvous = new Rendezvous(); 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; service.open(path, open_flag, null, new DoneOpen() { @Override public void doneOpen(IToken token, FileSystemException error, IFileHandle hdl) { errors[0] = error; handle = hdl; // Rendezvous rendezvous.arrive(); } }); try { rendezvous.waiting(openTimeout); } catch (InterruptedException e) { throw new IOException(Messages.TcfURLConnection_OpenFileTimeout); } 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.TcfURLConnection_NoFSServiceAvailable); } } } /* * (non-Javadoc) * * @see java.net.URLConnection#connect() */ @Override public void connect() throws IOException { if (!connected) { openFile(); if (doInput) { inputStream = new TcfInputStream(this); inputStream.setTimeout(getReadTimeout()); } if (doOutput) { outputStream = new TcfOutputStream(this); outputStream.setTimeout(getReadTimeout()); } 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) { final Rendezvous rendezvous = new Rendezvous(); IFileSystem service = handle.getService(); service.close(handle, new DoneClose() { @Override public void doneClose(IToken token, FileSystemException error) { rendezvous.arrive(); } }); try { rendezvous.waiting(closeTimeout); } catch (InterruptedException e) { throw new IOException(Messages.TcfURLConnection_CloseFileTimeout); } channel.close(); } } /** * 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; } }