1 /******************************************************************************* 2 * Copyright (c) 2000, 2010 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 *******************************************************************************/ 14 package org.eclipse.team.internal.ccvs.core.client; 15 16 import java.util.*; 17 18 import org.eclipse.core.runtime.*; 19 import org.eclipse.osgi.util.NLS; 20 import org.eclipse.team.core.TeamException; 21 import org.eclipse.team.internal.ccvs.core.*; 22 import org.eclipse.team.internal.ccvs.core.client.listeners.ICommandOutputListener; 23 import org.eclipse.team.internal.ccvs.core.connection.CVSServerException; 24 25 /** 26 * Abstract base class for requests that are to be sent to the server. 27 */ 28 public abstract class Request { 29 public static final ExpandModules EXPAND_MODULES = new ExpandModules(); 30 public static final ValidRequests VALID_REQUESTS = new ValidRequests(); 31 32 /*** Response handler map ***/ 33 private static final Map<String,ResponseHandler> responseHandlers = new HashMap<>(); 34 initializeHandlerCache()35 private static void initializeHandlerCache() { 36 synchronized(responseHandlers) { 37 registerResponseHandler(new CheckedInHandler()); 38 registerResponseHandler(new CopyHandler()); 39 registerResponseHandler(new ModTimeHandler()); 40 registerResponseHandler(new NewEntryHandler()); 41 registerResponseHandler(new RemovedHandler()); 42 registerResponseHandler(new RemoveEntryHandler()); 43 registerResponseHandler(new StaticHandler(true)); 44 registerResponseHandler(new StaticHandler(false)); 45 registerResponseHandler(new StickyHandler(true)); 46 registerResponseHandler(new StickyHandler(false)); 47 registerResponseHandler(new UpdatedHandler(UpdatedHandler.HANDLE_UPDATED)); 48 registerResponseHandler(new UpdatedHandler(UpdatedHandler.HANDLE_UPDATE_EXISTING)); 49 registerResponseHandler(new UpdatedHandler(UpdatedHandler.HANDLE_CREATED)); 50 registerResponseHandler(new UpdatedHandler(UpdatedHandler.HANDLE_MERGED)); 51 registerResponseHandler(new ValidRequestsHandler()); 52 registerResponseHandler(new ModuleExpansionHandler()); 53 registerResponseHandler(new MTHandler()); 54 registerResponseHandler(new NotifiedHandler()); 55 registerResponseHandler(new TemplateHandler()); 56 } 57 } registerResponseHandler(ResponseHandler handler)58 private static void registerResponseHandler(ResponseHandler handler) { 59 synchronized(responseHandlers) { 60 responseHandlers.put(handler.getResponseID(), handler); 61 } 62 } 63 64 /** 65 * This method is invoked by Session to get a mutable copy of the 66 * global list of acceptable response handlers. 67 * 68 * @return a map of response handlers 69 */ getReponseHandlerMap()70 protected static Map<String,ResponseHandler> getReponseHandlerMap() { 71 synchronized(responseHandlers) { 72 if (responseHandlers.isEmpty()) { 73 initializeHandlerCache(); 74 } 75 Map<String,ResponseHandler> copy = new HashMap<>(); 76 for (Iterator iter = responseHandlers.values().iterator(); iter.hasNext();) { 77 ResponseHandler handler = (ResponseHandler) iter.next(); 78 copy.put(handler.getResponseID(), handler.getInstance()); 79 } 80 return copy; 81 } 82 } 83 /** 84 * Prevents client code from instantiating us. 85 */ Request()86 protected Request() { } 87 88 /** 89 * Returns the string used to invoke this request on the server. 90 * [template method] 91 * 92 * @return the request identifier string 93 */ getRequestId()94 protected abstract String getRequestId(); 95 96 /** 97 * Executes a request and processes the responses. 98 * 99 * @param session the open CVS session 100 * @param listener the command output listener, or null to discard all messages 101 * @param monitor the progress monitor 102 * @return a status code indicating success or failure of the operation 103 */ executeRequest(Session session, ICommandOutputListener listener, IProgressMonitor monitor)104 protected IStatus executeRequest(Session session, ICommandOutputListener listener, 105 IProgressMonitor monitor) throws CVSException { 106 // send request 107 session.sendRequest(getRequestId()); 108 109 // This number can be tweaked if the monitor is judged to move too 110 // quickly or too slowly. After some experimentation this is a good 111 // number for both large projects (it doesn't move so quickly as to 112 // give a false sense of speed) and smaller projects (it actually does 113 // move some rather than remaining still and then jumping to 100). 114 final int TOTAL_WORK = 300; 115 monitor.beginTask(CVSMessages.Command_receivingResponses, TOTAL_WORK); 116 monitor.subTask(CVSMessages.Command_receivingResponses); 117 int halfWay = TOTAL_WORK / 2; 118 int currentIncrement = 4; 119 int nextProgress = currentIncrement; 120 int worked = 0; 121 122 // If the session is connected to a CVSNT server (1.11.1.1), we'll need to do some special handling for 123 // some errors. Unfortunately, CVSNT 1.11.1.1 will drop the connection after so some functionality is 124 // still affected 125 boolean isCVSNT = session.isCVSNT(); 126 127 session.clearErrors(); 128 for (;;) { 129 // update monitor work amount 130 if (--nextProgress <= 0) { 131 monitor.worked(1); 132 worked++; 133 if (worked >= halfWay) { 134 // we have passed the current halfway point, so double the 135 // increment and reset the halfway point. 136 currentIncrement *= 2; 137 halfWay += (TOTAL_WORK - halfWay) / 2; 138 } 139 // reset the progress counter to another full increment 140 nextProgress = currentIncrement; 141 } 142 Policy.checkCanceled(monitor); 143 144 // retrieve a response line 145 String response = session.readLine(); 146 int spacePos = response.indexOf(' '); 147 String argument; 148 if (spacePos != -1) { 149 argument = response.substring(spacePos + 1); 150 response = response.substring(0, spacePos); 151 } else argument = ""; //$NON-NLS-1$ 152 153 // handle completion responses 154 if (response.equals("ok")) { //$NON-NLS-1$ 155 break; 156 } else if (response.equals("error") || (isCVSNT && response.isEmpty())) { //$NON-NLS-1$ 157 argument = argument.trim(); 158 boolean serious = false; 159 if (argument.length() == 0) { 160 argument = getServerErrorMessage(); 161 } else { 162 argument = NLS.bind(CVSMessages.Command_seriousServerError, new String[] { argument }); 163 if (!session.hasErrors()) { 164 session.addError(new CVSStatus(IStatus.ERROR, CVSStatus.SERVER_ERROR, argument,session.getLocalRoot())); 165 } 166 serious = true; 167 } 168 169 if (!session.hasErrors()) { 170 session.addError(new CVSStatus(IStatus.ERROR, CVSStatus.SERVER_ERROR, CVSMessages.Command_noMoreInfoAvailable,session.getLocalRoot())); 171 } 172 IStatus status = new MultiStatus(CVSProviderPlugin.ID, CVSStatus.SERVER_ERROR, 173 session.getErrors(), 174 argument, null); 175 if (serious) { 176 throw new CVSServerException(status); 177 } else { 178 // look for particularly bad errors in the accumulated statuses 179 IStatus[] errors = session.getErrors(); 180 for (IStatus s : errors) { 181 if (s.getCode() == CVSStatus.PROTOCOL_ERROR) { 182 throw new CVSServerException(status); 183 } 184 } 185 } 186 return status; 187 // handle message responses 188 } else if (response.equals("MT")) { //$NON-NLS-1$ 189 // Handle the MT response 190 MTHandler handler = (MTHandler) session.getResponseHandler(response); 191 if (handler != null) { 192 handler.handle(session, argument, monitor); 193 } else { 194 throw new CVSException(new org.eclipse.core.runtime.Status(IStatus.ERROR, 195 CVSProviderPlugin.ID, TeamException.IO_FAILED, 196 NLS.bind(CVSMessages.Command_unsupportedResponse, new String[] { response, argument }), null)); 197 } 198 // If a line is available, pass it on to the message listener 199 // and console as if it were an M response 200 if (handler.isLineAvailable()) { 201 String line = handler.getLine(); 202 IStatus status = listener.messageLine(line, session.getCVSRepositoryLocation(), session.getLocalRoot(), monitor); 203 session.addError(status); // The session ignores OK status 204 ConsoleListeners.getInstance().messageLineReceived(session, line, status); 205 206 } 207 } else if (response.equals("M")) { //$NON-NLS-1$ 208 IStatus status = listener.messageLine(argument, session.getCVSRepositoryLocation(), session.getLocalRoot(), monitor); 209 session.addError(status); // The session ignores OK status 210 ConsoleListeners.getInstance().messageLineReceived(session, argument, status); 211 } else if (response.equals("E")) { //$NON-NLS-1$ 212 IStatus status = listener.errorLine(argument, session.getCVSRepositoryLocation(), session.getLocalRoot(), monitor); 213 session.addError(status); // The session ignores OK status 214 ConsoleListeners.getInstance().errorLineReceived(session, argument, status); 215 // handle other responses 216 } else { 217 ResponseHandler handler = session.getResponseHandler(response); 218 if (handler != null) { 219 handler.handle(session, argument, monitor); 220 } else { 221 throw new CVSException(new org.eclipse.core.runtime.Status(IStatus.ERROR, 222 CVSProviderPlugin.ID, TeamException.IO_FAILED, 223 NLS.bind(CVSMessages.Command_unsupportedResponse, new String[] { response, argument }), null)); 224 } 225 } 226 } 227 if (!session.hasErrors()) { 228 return ICommandOutputListener.OK; 229 } else { 230 return new MultiStatus(CVSProviderPlugin.ID, IStatus.INFO, 231 session.getErrors(), 232 NLS.bind(CVSMessages.Command_warnings, new String[] { getDisplayText() }), null); 233 } 234 } 235 236 /* 237 * Provide the message that is used for the status that is generated when the server 238 * reports as error. 239 */ getServerErrorMessage()240 protected String getServerErrorMessage() { 241 return NLS.bind(CVSMessages.Command_serverError, new String[] { getDisplayText() }); 242 } getDisplayText()243 protected String getDisplayText() { 244 return getRequestId(); 245 } 246 } 247