| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.team.internal.ccvs.core.client; |
| |
| import java.util.*; |
| |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.team.internal.ccvs.core.*; |
| import org.eclipse.team.internal.ccvs.core.client.listeners.ICommandOutputListener; |
| import org.eclipse.team.internal.ccvs.core.connection.CVSServerException; |
| |
| /** |
| * Abstract base class for requests that are to be sent to the server. |
| */ |
| public abstract class Request { |
| public static final ExpandModules EXPAND_MODULES = new ExpandModules(); |
| public static final ValidRequests VALID_REQUESTS = new ValidRequests(); |
| |
| /*** Response handler map ***/ |
| private static final Map responseHandlers = new HashMap(); |
| |
| private static void initializeHandlerCache() { |
| synchronized(responseHandlers) { |
| registerResponseHandler(new CheckedInHandler()); |
| registerResponseHandler(new CopyHandler()); |
| registerResponseHandler(new ModTimeHandler()); |
| registerResponseHandler(new NewEntryHandler()); |
| registerResponseHandler(new RemovedHandler()); |
| registerResponseHandler(new RemoveEntryHandler()); |
| registerResponseHandler(new StaticHandler(true)); |
| registerResponseHandler(new StaticHandler(false)); |
| registerResponseHandler(new StickyHandler(true)); |
| registerResponseHandler(new StickyHandler(false)); |
| registerResponseHandler(new UpdatedHandler(UpdatedHandler.HANDLE_UPDATED)); |
| registerResponseHandler(new UpdatedHandler(UpdatedHandler.HANDLE_UPDATE_EXISTING)); |
| registerResponseHandler(new UpdatedHandler(UpdatedHandler.HANDLE_CREATED)); |
| registerResponseHandler(new UpdatedHandler(UpdatedHandler.HANDLE_MERGED)); |
| registerResponseHandler(new ValidRequestsHandler()); |
| registerResponseHandler(new ModuleExpansionHandler()); |
| registerResponseHandler(new MTHandler()); |
| registerResponseHandler(new NotifiedHandler()); |
| registerResponseHandler(new TemplateHandler()); |
| } |
| } |
| private static void registerResponseHandler(ResponseHandler handler) { |
| synchronized(responseHandlers) { |
| responseHandlers.put(handler.getResponseID(), handler); |
| } |
| } |
| |
| /** |
| * This method is invoked by Session to get a mutable copy of the |
| * global list of acceptable response handlers. |
| * |
| * @return a map of reponse handlers |
| */ |
| protected static Map getReponseHandlerMap() { |
| synchronized(responseHandlers) { |
| if (responseHandlers.isEmpty()) { |
| initializeHandlerCache(); |
| } |
| Map copy = new HashMap(); |
| for (Iterator iter = responseHandlers.values().iterator(); iter.hasNext();) { |
| ResponseHandler handler = (ResponseHandler) iter.next(); |
| copy.put(handler.getResponseID(), handler.getInstance()); |
| |
| } |
| return copy; |
| } |
| } |
| /** |
| * Prevents client code from instantiating us. |
| */ |
| protected Request() { } |
| |
| /** |
| * Returns the string used to invoke this request on the server. |
| * [template method] |
| * |
| * @return the request identifier string |
| */ |
| protected abstract String getRequestId(); |
| |
| /** |
| * Executes a request and processes the responses. |
| * |
| * @param session the open CVS session |
| * @param listener the command output listener, or null to discard all messages |
| * @param monitor the progress monitor |
| * @return a status code indicating success or failure of the operation |
| */ |
| protected IStatus executeRequest(Session session, ICommandOutputListener listener, |
| IProgressMonitor monitor) throws CVSException { |
| // send request |
| session.sendRequest(getRequestId()); |
| |
| // This number can be tweaked if the monitor is judged to move too |
| // quickly or too slowly. After some experimentation this is a good |
| // number for both large projects (it doesn't move so quickly as to |
| // give a false sense of speed) and smaller projects (it actually does |
| // move some rather than remaining still and then jumping to 100). |
| final int TOTAL_WORK = 300; |
| monitor.beginTask(CVSMessages.Command_receivingResponses, TOTAL_WORK); //$NON-NLS-1$ |
| monitor.subTask(CVSMessages.Command_receivingResponses); //$NON-NLS-1$ |
| int halfWay = TOTAL_WORK / 2; |
| int currentIncrement = 4; |
| int nextProgress = currentIncrement; |
| int worked = 0; |
| |
| // If the session is connected to a CVSNT server (1.11.1.1), we'll need to do some special handling for |
| // some errors. Unfortunately, CVSNT 1.11.1.1 will drop the connection after so some functionality is |
| // still effected |
| boolean isCVSNT = session.isCVSNT(); |
| |
| session.clearErrors(); |
| for (;;) { |
| // update monitor work amount |
| if (--nextProgress <= 0) { |
| monitor.worked(1); |
| worked++; |
| if (worked >= halfWay) { |
| // we have passed the current halfway point, so double the |
| // increment and reset the halfway point. |
| currentIncrement *= 2; |
| halfWay += (TOTAL_WORK - halfWay) / 2; |
| } |
| // reset the progress counter to another full increment |
| nextProgress = currentIncrement; |
| } |
| Policy.checkCanceled(monitor); |
| |
| // retrieve a response line |
| String response = session.readLine(); |
| int spacePos = response.indexOf(' '); |
| String argument; |
| if (spacePos != -1) { |
| argument = response.substring(spacePos + 1); |
| response = response.substring(0, spacePos); |
| } else argument = ""; //$NON-NLS-1$ |
| |
| // handle completion responses |
| if (response.equals("ok")) { //$NON-NLS-1$ |
| break; |
| } else if (response.equals("error") || (isCVSNT && response.equals(""))) { //$NON-NLS-1$ //$NON-NLS-2$ |
| argument = argument.trim(); |
| boolean serious = false; |
| if (argument.length() == 0) { |
| argument = getServerErrorMessage(); |
| } else { |
| argument = NLS.bind(CVSMessages.Command_seriousServerError, new String[] { argument }); //$NON-NLS-1$ |
| if (!session.hasErrors()) { |
| session.addError(new CVSStatus(CVSStatus.ERROR, CVSStatus.SERVER_ERROR, argument)); |
| } |
| serious = true; |
| } |
| |
| if (!session.hasErrors()) { |
| session.addError(new CVSStatus(CVSStatus.ERROR, CVSStatus.SERVER_ERROR, CVSMessages.Command_noMoreInfoAvailable));//$NON-NLS-1$ |
| } |
| IStatus status = new MultiStatus(CVSProviderPlugin.ID, CVSStatus.SERVER_ERROR, |
| session.getErrors(), |
| argument, null); |
| if (serious) { |
| throw new CVSServerException(status); |
| } else { |
| // look for particularly bad errors in the accumulated statii |
| IStatus[] errors = session.getErrors(); |
| for (int i = 0; i < errors.length; i++) { |
| IStatus s = errors[i]; |
| if (s.getCode() == CVSStatus.PROTOCOL_ERROR) { |
| throw new CVSServerException(status); |
| } |
| } |
| } |
| return status; |
| // handle message responses |
| } else if (response.equals("MT")) { //$NON-NLS-1$ |
| // Handle the MT response |
| MTHandler handler = (MTHandler) session.getResponseHandler(response); |
| if (handler != null) { |
| handler.handle(session, argument, monitor); |
| } else { |
| throw new CVSException(new org.eclipse.core.runtime.Status(IStatus.ERROR, |
| CVSProviderPlugin.ID, CVSException.IO_FAILED, |
| NLS.bind(CVSMessages.Command_unsupportedResponse, new String[] { response, argument }), null)); //$NON-NLS-1$ |
| } |
| // If a line is available, pass it on to the message listener |
| // and console as if it were an M response |
| if (handler.isLineAvailable()) { |
| String line = handler.getLine(); |
| IStatus status = listener.messageLine(line, session.getCVSRepositoryLocation(), session.getLocalRoot(), monitor); |
| session.addError(status); // The session ignores OK status |
| ConsoleListeners.getInstance().messageLineReceived(session, line, status); |
| |
| } |
| } else if (response.equals("M")) { //$NON-NLS-1$ |
| IStatus status = listener.messageLine(argument, session.getCVSRepositoryLocation(), session.getLocalRoot(), monitor); |
| session.addError(status); // The session ignores OK status |
| ConsoleListeners.getInstance().messageLineReceived(session, argument, status); |
| } else if (response.equals("E")) { //$NON-NLS-1$ |
| IStatus status = listener.errorLine(argument, session.getCVSRepositoryLocation(), session.getLocalRoot(), monitor); |
| session.addError(status); // The session ignores OK status |
| ConsoleListeners.getInstance().errorLineReceived(session, argument, status); |
| // handle other responses |
| } else { |
| ResponseHandler handler = session.getResponseHandler(response); |
| if (handler != null) { |
| handler.handle(session, argument, monitor); |
| } else { |
| throw new CVSException(new org.eclipse.core.runtime.Status(IStatus.ERROR, |
| CVSProviderPlugin.ID, CVSException.IO_FAILED, |
| NLS.bind(CVSMessages.Command_unsupportedResponse, new String[] { response, argument }), null)); //$NON-NLS-1$ |
| } |
| } |
| } |
| if (!session.hasErrors()) { |
| return ICommandOutputListener.OK; |
| } else { |
| return new MultiStatus(CVSProviderPlugin.ID, CVSStatus.INFO, |
| session.getErrors(), |
| NLS.bind(CVSMessages.Command_warnings, new String[] { getDisplayText() }), null); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| |
| /* |
| * Provide the message that is used for the status that is generated when the server |
| * reports as error. |
| */ |
| protected String getServerErrorMessage() { |
| return NLS.bind(CVSMessages.Command_serverError, new String[] { getDisplayText() }); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| protected String getDisplayText() { |
| return getRequestId(); |
| } |
| } |