| /******************************************************************************* |
| * Copyright (c) 2000, 2014 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Brock Janiczak (brockj@tpg.com.au) - Bug 144419 Avoid calculating encoding for each line read/written |
| *******************************************************************************/ |
| package org.eclipse.team.internal.ccvs.core.connection; |
| |
| |
| import java.io.*; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.team.internal.ccvs.core.*; |
| |
| /** |
| * A connection to talk to a cvs server. The life cycle of a connection is |
| * as follows: |
| * <ul> |
| * <li> constructor: creates a new connection object that wraps the given |
| * repository location and connection method. |
| * <li> open: opens a connection. |
| * <li> send a request: use write* method or use the request stream directly. |
| * <code>GetRequestStream</code> returns an output stream to directly |
| * talk to the server. |
| * <li> read responses: use read* methods or use the response stream directly. |
| * <code>GetResponseStream</code> returns an input stream to directly |
| * read output from the server. |
| * <li> close: closes the connection. A closed connection can be reopened by |
| * calling open again. |
| * </ul> |
| */ |
| public class Connection { |
| private static final byte NEWLINE= 0xA; |
| |
| private IServerConnection serverConnection; |
| private ICVSRepositoryLocation fCVSRoot; |
| private boolean fIsEstablished; |
| private InputStream fResponseStream; |
| private String fServerEncoding; |
| private byte[] readLineBuffer = new byte[256]; |
| |
| public Connection(ICVSRepositoryLocation cvsroot, IServerConnection serverConnection) { |
| fCVSRoot = cvsroot; |
| this.serverConnection = serverConnection; |
| fServerEncoding = getEncoding(fCVSRoot); |
| } |
| |
| private static byte[] append(byte[] buffer, int index, byte b) { |
| if (index >= buffer.length) { |
| byte[] newBuffer= new byte[index * 2]; |
| System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); |
| buffer= newBuffer; |
| } |
| buffer[index]= b; |
| return buffer; |
| } |
| /** |
| * Closes the connection. |
| */ |
| public void close() { |
| if (!isEstablished()) |
| return; |
| try { |
| serverConnection.close(); |
| } catch (IOException ex) { |
| // Generally, errors on close are of no interest. |
| // However, log them if debugging is on |
| if (Policy.DEBUG) { |
| CVSProviderPlugin.log(new CVSCommunicationException(CVSMessages.Connection_cannotClose, fCVSRoot, ex)); |
| } |
| } finally { |
| fResponseStream = null; |
| fIsEstablished = false; |
| } |
| } |
| /** |
| * Flushes the request stream. |
| */ |
| public void flush() throws CVSException { |
| if (!isEstablished()) |
| return; |
| try { |
| getOutputStream().flush(); |
| } catch(IOException e) { |
| throw new CVSCommunicationException(fCVSRoot,e); |
| } |
| } |
| |
| /** |
| * Returns the <code>OutputStream</code> used to send requests |
| * to the server. |
| */ |
| public OutputStream getOutputStream() { |
| if (!isEstablished()) |
| return null; |
| return serverConnection.getOutputStream(); |
| } |
| /** |
| * Returns the <code>InputStream</code> used to read responses from |
| * the server. |
| */ |
| public InputStream getInputStream() { |
| if (!isEstablished()) |
| return null; |
| if (fResponseStream == null) |
| fResponseStream = serverConnection.getInputStream(); |
| return fResponseStream; |
| } |
| |
| /** |
| * Returns <code>true</code> if the connection is established; |
| * otherwise <code>false</code>. |
| */ |
| public boolean isEstablished() { |
| return fIsEstablished; |
| } |
| |
| /** |
| * Opens the connection. |
| */ |
| public void open(IProgressMonitor monitor) throws CVSException { |
| if (isEstablished()) |
| return; |
| try { |
| serverConnection.open(monitor); |
| } catch (IOException e) { |
| throw new CVSCommunicationException(NLS.bind(CVSMessages.Connection_0, new String[] { fCVSRoot.getLocation(true), CVSCommunicationException.getMessageFor(e) }), fCVSRoot, e); |
| } |
| fIsEstablished= true; |
| } |
| /** |
| * Reads a line from the response stream. |
| */ |
| public String readLine() throws CVSException { |
| if (!isEstablished()) |
| throw new CVSCommunicationException(CVSMessages.Connection_readUnestablishedConnection,fCVSRoot,null); |
| try { |
| InputStream in = getInputStream(); |
| int index = 0; |
| int r; |
| while ((r = in.read()) != -1) { |
| if (r == NEWLINE) break; |
| readLineBuffer = append(readLineBuffer, index++, (byte) r); |
| } |
| |
| String result = new String(readLineBuffer, 0, index, fServerEncoding); |
| if (Policy.isDebugProtocol()) Policy.printProtocolLine(result); |
| return result; |
| } catch (IOException e) { |
| throw new CVSCommunicationException(fCVSRoot,e); |
| } |
| } |
| |
| static String readLine(ICVSRepositoryLocation location, InputStream in) throws IOException { |
| byte[] buffer = new byte[256]; |
| int index = 0; |
| int r; |
| while ((r = in.read()) != -1) { |
| if (r == NEWLINE) |
| break; |
| buffer = append(buffer, index++, (byte) r); |
| } |
| |
| String result = new String(buffer, 0, index, getEncoding(location)); |
| if (Policy.isDebugProtocol()) |
| Policy.printProtocolLine(result); |
| return result; |
| } |
| |
| //---- Helper to send strings to the server ---------------------------- |
| |
| /** |
| * Sends the given string to the server. |
| */ |
| public void write(String s) throws CVSException { |
| try { |
| write(s.getBytes(fServerEncoding), false); |
| } catch (UnsupportedEncodingException e) { |
| IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.SERVER_ERROR, e.getMessage(), e, fCVSRoot); |
| throw new CVSException (status); |
| } |
| } |
| |
| /** |
| * Return the encoding for the given repository location |
| * @return the encoding for the given repository location |
| */ |
| public static String getEncoding(ICVSRepositoryLocation location) { |
| return location.getEncoding(); |
| } |
| |
| /** |
| * Sends the given string and a newline to the server. |
| */ |
| public void writeLine(String s) throws CVSException { |
| try { |
| write(s.getBytes(fServerEncoding), true); |
| } catch (UnsupportedEncodingException e) { |
| IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.SERVER_ERROR, e.getMessage(), e, fCVSRoot); |
| throw new CVSException (status); |
| } |
| } |
| |
| void write (byte[] bytes, boolean newLine) throws CVSException { |
| write(bytes, 0, bytes.length, newLine); |
| } |
| |
| /** |
| * Low level method to write a string to the server. All write* methods are |
| * funneled through this method. |
| */ |
| void write(byte[] b, int off, int len, boolean newline) throws CVSException { |
| if (!isEstablished()) |
| throw new CVSCommunicationException(CVSMessages.Connection_writeUnestablishedConnection,fCVSRoot,null); |
| |
| if (Policy.isDebugProtocol()) |
| Policy.printProtocol(new String(b, off, len), newline); |
| |
| try { |
| OutputStream out= getOutputStream(); |
| out.write(b, off, len); |
| if (newline) |
| out.write(NEWLINE); |
| |
| } catch (IOException e) { |
| throw new CVSCommunicationException(fCVSRoot,e); |
| } |
| } |
| } |