blob: 341ed488daf886ee6323de63fe65e9632ff33b41 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 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
*******************************************************************************/
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.core.TeamException;
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 response 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);
monitor.subTask(CVSMessages.Command_receivingResponses);
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 affected
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 });
if (!session.hasErrors()) {
session.addError(new CVSStatus(IStatus.ERROR, CVSStatus.SERVER_ERROR, argument,session.getLocalRoot()));
}
serious = true;
}
if (!session.hasErrors()) {
session.addError(new CVSStatus(IStatus.ERROR, CVSStatus.SERVER_ERROR, CVSMessages.Command_noMoreInfoAvailable,session.getLocalRoot()));
}
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 statuses
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, TeamException.IO_FAILED,
NLS.bind(CVSMessages.Command_unsupportedResponse, new String[] { response, argument }), null));
}
// 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, TeamException.IO_FAILED,
NLS.bind(CVSMessages.Command_unsupportedResponse, new String[] { response, argument }), null));
}
}
}
if (!session.hasErrors()) {
return ICommandOutputListener.OK;
} else {
return new MultiStatus(CVSProviderPlugin.ID, IStatus.INFO,
session.getErrors(),
NLS.bind(CVSMessages.Command_warnings, new String[] { getDisplayText() }), null);
}
}
/*
* 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() });
}
protected String getDisplayText() {
return getRequestId();
}
}