blob: 5066ce408b0f9b8b5ec7719d1e18fe823c099a6b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.ccvs.core.client;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
import org.eclipse.team.internal.ccvs.core.CVSStatus;
import org.eclipse.team.internal.ccvs.core.Policy;
import org.eclipse.team.internal.ccvs.core.client.listeners.ICommandOutputListener;
import org.eclipse.team.internal.ccvs.core.client.listeners.IConsoleListener;
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(Policy.bind("Command.receivingResponses"), TOTAL_WORK); //$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();
List accumulatedStatus = new ArrayList();
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 = Policy.bind("Command.seriousServerError", argument); //$NON-NLS-1$
if (!accumulatedStatus.isEmpty()) {
accumulatedStatus.add(new CVSStatus(CVSStatus.ERROR, CVSStatus.SERVER_ERROR, argument));
}
serious = true;
}
if (accumulatedStatus.isEmpty()) {
accumulatedStatus.add(new CVSStatus(CVSStatus.ERROR, CVSStatus.SERVER_ERROR, Policy.bind("Command.noMoreInfoAvailable")));//$NON-NLS-1$
}
IStatus status = new MultiStatus(CVSProviderPlugin.ID, CVSStatus.SERVER_ERROR,
(IStatus[]) accumulatedStatus.toArray(new IStatus[accumulatedStatus.size()]),
argument, null);
if (serious) {
throw new CVSServerException(status);
} else {
// look for particularly bad errors in the accumulated statii
for (Iterator iter = accumulatedStatus.iterator(); iter.hasNext();) {
IStatus s = (IStatus) iter.next();
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,
Policy.bind("Command.unsupportedResponse", 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);
if (status != ICommandOutputListener.OK) accumulatedStatus.add(status);
if (session.isOutputToConsole()) {
IConsoleListener consoleListener = CVSProviderPlugin.getPlugin().getConsoleListener();
if (consoleListener != null) consoleListener.messageLineReceived(line);
}
}
} else if (response.equals("M")) { //$NON-NLS-1$
IStatus status = listener.messageLine(argument, session.getCVSRepositoryLocation(), session.getLocalRoot(), monitor);
if (status != ICommandOutputListener.OK) accumulatedStatus.add(status);
if (session.isOutputToConsole()) {
IConsoleListener consoleListener = CVSProviderPlugin.getPlugin().getConsoleListener();
if (consoleListener != null) consoleListener.messageLineReceived(argument);
}
} else if (response.equals("E")) { //$NON-NLS-1$
IStatus status = listener.errorLine(argument, session.getCVSRepositoryLocation(), session.getLocalRoot(), monitor);
if (status != ICommandOutputListener.OK) accumulatedStatus.add(status);
if (session.isOutputToConsole()) {
IConsoleListener consoleListener = CVSProviderPlugin.getPlugin().getConsoleListener();
if (consoleListener != null) consoleListener.errorLineReceived(argument);
}
// handle other responses
} else {
ResponseHandler handler = (ResponseHandler) 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,
Policy.bind("Command.unsupportedResponse", response, argument), null)); //$NON-NLS-1$
}
}
}
if (accumulatedStatus.isEmpty()) {
return ICommandOutputListener.OK;
} else {
return new MultiStatus(CVSProviderPlugin.ID, CVSStatus.INFO,
(IStatus[]) accumulatedStatus.toArray(new IStatus[accumulatedStatus.size()]),
Policy.bind("Command.warnings", Policy.bind("Command." + getRequestId())), 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 Policy.bind("Command.serverError", Policy.bind("Command." + getRequestId())); //$NON-NLS-1$ //$NON-NLS-2$
}
}