1 /******************************************************************************* 2 * Copyright (c) 2000, 2014 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Brock Janiczak (brockj@tpg.com.au) - Bug 144419 Avoid calculating encoding for each line read/written 14 *******************************************************************************/ 15 package org.eclipse.team.internal.ccvs.core.connection; 16 17 18 import java.io.*; 19 20 import org.eclipse.core.runtime.IProgressMonitor; 21 import org.eclipse.core.runtime.IStatus; 22 import org.eclipse.osgi.util.NLS; 23 import org.eclipse.team.internal.ccvs.core.*; 24 25 /** 26 * A connection to talk to a cvs server. The life cycle of a connection is 27 * as follows: 28 * <ul> 29 * <li> constructor: creates a new connection object that wraps the given 30 * repository location and connection method. 31 * <li> open: opens a connection. 32 * <li> send a request: use write* method or use the request stream directly. 33 * <code>GetRequestStream</code> returns an output stream to directly 34 * talk to the server. 35 * <li> read responses: use read* methods or use the response stream directly. 36 * <code>GetResponseStream</code> returns an input stream to directly 37 * read output from the server. 38 * <li> close: closes the connection. A closed connection can be reopened by 39 * calling open again. 40 * </ul> 41 */ 42 public class Connection { 43 private static final byte NEWLINE= 0xA; 44 45 private IServerConnection serverConnection; 46 private ICVSRepositoryLocation fCVSRoot; 47 private boolean fIsEstablished; 48 private InputStream fResponseStream; 49 private String fServerEncoding; 50 private byte[] readLineBuffer = new byte[256]; 51 Connection(ICVSRepositoryLocation cvsroot, IServerConnection serverConnection)52 public Connection(ICVSRepositoryLocation cvsroot, IServerConnection serverConnection) { 53 fCVSRoot = cvsroot; 54 this.serverConnection = serverConnection; 55 fServerEncoding = getEncoding(fCVSRoot); 56 } 57 append(byte[] buffer, int index, byte b)58 private static byte[] append(byte[] buffer, int index, byte b) { 59 if (index >= buffer.length) { 60 byte[] newBuffer= new byte[index * 2]; 61 System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); 62 buffer= newBuffer; 63 } 64 buffer[index]= b; 65 return buffer; 66 } 67 /** 68 * Closes the connection. 69 */ close()70 public void close() { 71 if (!isEstablished()) 72 return; 73 try { 74 serverConnection.close(); 75 } catch (IOException ex) { 76 // Generally, errors on close are of no interest. 77 // However, log them if debugging is on 78 if (Policy.DEBUG) { 79 CVSProviderPlugin.log(new CVSCommunicationException(CVSMessages.Connection_cannotClose, fCVSRoot, ex)); 80 } 81 } finally { 82 fResponseStream = null; 83 fIsEstablished = false; 84 } 85 } 86 /** 87 * Flushes the request stream. 88 */ flush()89 public void flush() throws CVSException { 90 if (!isEstablished()) 91 return; 92 try { 93 getOutputStream().flush(); 94 } catch(IOException e) { 95 throw new CVSCommunicationException(fCVSRoot,e); 96 } 97 } 98 99 /** 100 * Returns the <code>OutputStream</code> used to send requests 101 * to the server. 102 */ getOutputStream()103 public OutputStream getOutputStream() { 104 if (!isEstablished()) 105 return null; 106 return serverConnection.getOutputStream(); 107 } 108 /** 109 * Returns the <code>InputStream</code> used to read responses from 110 * the server. 111 */ getInputStream()112 public InputStream getInputStream() { 113 if (!isEstablished()) 114 return null; 115 if (fResponseStream == null) 116 fResponseStream = serverConnection.getInputStream(); 117 return fResponseStream; 118 } 119 120 /** 121 * Returns <code>true</code> if the connection is established; 122 * otherwise <code>false</code>. 123 */ isEstablished()124 public boolean isEstablished() { 125 return fIsEstablished; 126 } 127 128 /** 129 * Opens the connection. 130 */ open(IProgressMonitor monitor)131 public void open(IProgressMonitor monitor) throws CVSException { 132 if (isEstablished()) 133 return; 134 try { 135 serverConnection.open(monitor); 136 } catch (IOException e) { 137 throw new CVSCommunicationException(NLS.bind(CVSMessages.Connection_0, new String[] { fCVSRoot.getLocation(true), CVSCommunicationException.getMessageFor(e) }), fCVSRoot, e); 138 } 139 fIsEstablished= true; 140 } 141 /** 142 * Reads a line from the response stream. 143 */ readLine()144 public String readLine() throws CVSException { 145 if (!isEstablished()) 146 throw new CVSCommunicationException(CVSMessages.Connection_readUnestablishedConnection,fCVSRoot,null); 147 try { 148 InputStream in = getInputStream(); 149 int index = 0; 150 int r; 151 while ((r = in.read()) != -1) { 152 if (r == NEWLINE) break; 153 readLineBuffer = append(readLineBuffer, index++, (byte) r); 154 } 155 156 String result = new String(readLineBuffer, 0, index, fServerEncoding); 157 if (Policy.isDebugProtocol()) Policy.printProtocolLine(result); 158 return result; 159 } catch (IOException e) { 160 throw new CVSCommunicationException(fCVSRoot,e); 161 } 162 } 163 readLine(ICVSRepositoryLocation location, InputStream in)164 static String readLine(ICVSRepositoryLocation location, InputStream in) throws IOException { 165 byte[] buffer = new byte[256]; 166 int index = 0; 167 int r; 168 while ((r = in.read()) != -1) { 169 if (r == NEWLINE) 170 break; 171 buffer = append(buffer, index++, (byte) r); 172 } 173 174 String result = new String(buffer, 0, index, getEncoding(location)); 175 if (Policy.isDebugProtocol()) 176 Policy.printProtocolLine(result); 177 return result; 178 } 179 180 //---- Helper to send strings to the server ---------------------------- 181 182 /** 183 * Sends the given string to the server. 184 */ write(String s)185 public void write(String s) throws CVSException { 186 try { 187 write(s.getBytes(fServerEncoding), false); 188 } catch (UnsupportedEncodingException e) { 189 IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.SERVER_ERROR, e.getMessage(), e, fCVSRoot); 190 throw new CVSException (status); 191 } 192 } 193 194 /** 195 * Return the encoding for the given repository location 196 * @return the encoding for the given repository location 197 */ getEncoding(ICVSRepositoryLocation location)198 public static String getEncoding(ICVSRepositoryLocation location) { 199 return location.getEncoding(); 200 } 201 202 /** 203 * Sends the given string and a newline to the server. 204 */ writeLine(String s)205 public void writeLine(String s) throws CVSException { 206 try { 207 write(s.getBytes(fServerEncoding), true); 208 } catch (UnsupportedEncodingException e) { 209 IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.SERVER_ERROR, e.getMessage(), e, fCVSRoot); 210 throw new CVSException (status); 211 } 212 } 213 write(byte[] bytes, boolean newLine)214 void write (byte[] bytes, boolean newLine) throws CVSException { 215 write(bytes, 0, bytes.length, newLine); 216 } 217 218 /** 219 * Low level method to write a string to the server. All write* methods are 220 * funneled through this method. 221 */ write(byte[] b, int off, int len, boolean newline)222 void write(byte[] b, int off, int len, boolean newline) throws CVSException { 223 if (!isEstablished()) 224 throw new CVSCommunicationException(CVSMessages.Connection_writeUnestablishedConnection,fCVSRoot,null); 225 226 if (Policy.isDebugProtocol()) 227 Policy.printProtocol(new String(b, off, len), newline); 228 229 try { 230 OutputStream out= getOutputStream(); 231 out.write(b, off, len); 232 if (newline) 233 out.write(NEWLINE); 234 235 } catch (IOException e) { 236 throw new CVSCommunicationException(fCVSRoot,e); 237 } 238 } 239 } 240