| /* |
| * Sun Public License Notice |
| * |
| * The contents of this file are subject to the Sun Public License |
| * Version 1.0 (the "License"). You may not use this file except in |
| * compliance with the License. A copy of the License is available at |
| * http://www.sun.com/ |
| * |
| * The Original Code is NetBeans. The Initial Developer of the Original |
| * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun |
| * Microsystems, Inc. All Rights Reserved. |
| */ |
| package org.netbeans.lib.cvsclient; |
| |
| import com.intellij.util.concurrency.Semaphore; |
| import org.jetbrains.annotations.NonNls; |
| import org.netbeans.lib.cvsclient.command.CommandAbortedException; |
| import org.netbeans.lib.cvsclient.command.CommandException; |
| import org.netbeans.lib.cvsclient.command.IGlobalOptions; |
| import org.netbeans.lib.cvsclient.command.IOCommandException; |
| import org.netbeans.lib.cvsclient.connection.AuthenticationException; |
| import org.netbeans.lib.cvsclient.event.IEventSender; |
| import org.netbeans.lib.cvsclient.file.FileDetails; |
| import org.netbeans.lib.cvsclient.file.FileObject; |
| import org.netbeans.lib.cvsclient.io.IStreamLogger; |
| import org.netbeans.lib.cvsclient.progress.sending.IRequestsProgressHandler; |
| import org.netbeans.lib.cvsclient.request.*; |
| import org.netbeans.lib.cvsclient.response.DefaultResponseHandler; |
| import org.netbeans.lib.cvsclient.response.IResponseHandler; |
| import org.netbeans.lib.cvsclient.response.ResponseParser; |
| import org.netbeans.lib.cvsclient.response.ValidRequestsResponseHandler; |
| import org.netbeans.lib.cvsclient.util.BugLog; |
| |
| import java.io.IOException; |
| import java.io.Reader; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.Future; |
| |
| /** |
| * @author Thomas Singer |
| */ |
| public final class RequestProcessor implements IRequestProcessor { |
| |
| // Fields ================================================================= |
| |
| private final IGlobalOptions globalOptions; |
| private final IClientEnvironment clientEnvironment; |
| private final ResponseService responseServices; |
| private final IStreamLogger streamLogger; |
| private final ICvsCommandStopper commandStopper; |
| @NonNls private static final String OS_NAME_PROPERTY = "os.name"; |
| @NonNls private static final String WINDOWS_PREFIX = "Windows"; |
| @NonNls private static final String CASE_REQUEST = "Case"; |
| @NonNls private static final String CVS_PASS_ENV_VARS_PROPERTY = "cvs.pass.env.vars"; |
| @NonNls private static final String NO = "no"; |
| private final long myTimeout; |
| |
| // Setup ================================================================== |
| public RequestProcessor(IClientEnvironment clientEnvironment, |
| IGlobalOptions globalOptions, |
| IEventSender eventSender, |
| IStreamLogger streamLogger, |
| ICvsCommandStopper commandStopper) { |
| this(clientEnvironment, globalOptions, eventSender, streamLogger, commandStopper, -1); |
| } |
| |
| public RequestProcessor(IClientEnvironment clientEnvironment, |
| IGlobalOptions globalOptions, |
| IEventSender eventSender, |
| IStreamLogger streamLogger, |
| ICvsCommandStopper commandStopper, final long timeout) { |
| myTimeout = timeout; |
| BugLog.getInstance().assertNotNull(globalOptions); |
| BugLog.getInstance().assertNotNull(clientEnvironment); |
| BugLog.getInstance().assertNotNull(eventSender); |
| BugLog.getInstance().assertNotNull(streamLogger); |
| BugLog.getInstance().assertNotNull(commandStopper); |
| |
| this.globalOptions = globalOptions; |
| this.clientEnvironment = clientEnvironment; |
| this.responseServices = new ResponseService(eventSender); |
| this.streamLogger = streamLogger; |
| this.commandStopper = commandStopper; |
| } |
| |
| // Implemented ============================================================ |
| |
| public boolean processRequests(Requests requests, IRequestsProgressHandler communicationProgressHandler) throws CommandException, |
| AuthenticationException { |
| IConnectionStreams connectionStreams = openConnection(); |
| try { |
| return processRequests(requests, connectionStreams, communicationProgressHandler); |
| } |
| finally { |
| connectionStreams.close(); |
| } |
| } |
| |
| // Utils ================================================================== |
| |
| private IConnectionStreams openConnection() throws CommandException, AuthenticationException { |
| clientEnvironment.getConnection().open(streamLogger); |
| |
| ConnectionStreams connectionStreams = |
| new ConnectionStreams(clientEnvironment.getConnection(), streamLogger, clientEnvironment.getCharset()); |
| boolean exception = true; |
| try { |
| updateValidRequests(connectionStreams); |
| |
| sendRequest(new RootRequest(clientEnvironment.getConnection().getRepository()), connectionStreams); |
| sendSetRequests(globalOptions, connectionStreams); |
| // Handle gzip-compression |
| if (globalOptions.isUseGzip() && isValidRequest(GzipStreamRequest.REQUEST)) { |
| sendRequest(new GzipStreamRequest(), connectionStreams); |
| |
| connectionStreams.setGzipped(); |
| } |
| |
| //TODO: set variables |
| |
| sendRequest(new ValidResponsesRequest(), connectionStreams); |
| |
| sendRequest(new UseUnchangedRequest(), connectionStreams); |
| sendGlobalOptionRequests(globalOptions, connectionStreams); |
| |
| if (System.getProperty(OS_NAME_PROPERTY).startsWith(WINDOWS_PREFIX) && isValidRequest(CASE_REQUEST)) { |
| sendRequest(new CaseRequest(), connectionStreams); |
| } |
| |
| exception = false; |
| return connectionStreams; |
| } |
| catch (IOException ex) { |
| BugLog.getInstance().showException(ex); |
| throw new IOCommandException(ex); |
| } |
| finally { |
| if (exception) { |
| connectionStreams.close(); |
| } |
| } |
| } |
| |
| private void sendSetRequests(IGlobalOptions globalOptions, ConnectionStreams connectionStreams) |
| throws CommandAbortedException, IOException { |
| Map envVariables = globalOptions.getEnvVariables(); |
| if (envVariables == null) { |
| return; |
| } |
| for (Iterator iterator = envVariables.keySet().iterator(); iterator.hasNext();) { |
| String varName = (String)iterator.next(); |
| String varValue = (String)envVariables.get(varName); |
| sendRequest(new SetRequest(varName, varValue), connectionStreams); |
| } |
| } |
| |
| private boolean processRequests(final Requests requests, |
| final IConnectionStreams connectionStreams, |
| final IRequestsProgressHandler communicationProgressHandler) |
| throws CommandException, IOCommandException { |
| |
| BugLog.getInstance().assertNotNull(requests); |
| |
| /*final ProcessRequestsHelper helper = (myTimeout == -1) ? |
| new DirectProcessRequestHelper() : new TimedOutProcessRequestHelper();*/ |
| final ProcessRequestsHelper helper = new DirectProcessRequestHelper(); |
| return helper.processRequests(requests, connectionStreams, communicationProgressHandler); |
| } |
| |
| private abstract class ProcessRequestsHelper { |
| protected IOException myIOException; |
| protected CommandException myCommandException; |
| protected boolean myResult; |
| |
| protected abstract void before(); |
| protected abstract void callRunnable(final Runnable runnable); |
| protected abstract void afterInRunnable(); |
| protected abstract void after() throws CommandException; |
| |
| public boolean processRequests(final Requests requests, |
| final IConnectionStreams connectionStreams, |
| final IRequestsProgressHandler communicationProgressHandler) |
| throws CommandException, IOCommandException { |
| final Runnable runnable = new Runnable() { |
| public void run() { |
| try { |
| checkCanceled(); |
| sendRequests(requests, connectionStreams, communicationProgressHandler); |
| checkCanceled(); |
| |
| sendRequest(requests.getResponseExpectingRequest(), connectionStreams); |
| connectionStreams.flushForReading(); |
| |
| myResult = handleResponses(connectionStreams, new DefaultResponseHandler()); |
| } |
| catch (IOException e) { |
| myIOException = e; |
| } |
| catch (CommandException e) { |
| myCommandException = e; |
| } |
| finally { |
| afterInRunnable(); |
| } |
| } |
| }; |
| |
| before(); |
| callRunnable(runnable); |
| if (myIOException != null) throw new IOCommandException(myIOException); |
| if (myCommandException != null) throw myCommandException; |
| after(); |
| |
| return myResult; |
| } |
| } |
| |
| private class TimedOutProcessRequestHelper extends ProcessRequestsHelper { |
| private final Semaphore mySemaphore; |
| private Future<?> myFuture; |
| |
| private TimedOutProcessRequestHelper() { |
| mySemaphore = new Semaphore(); |
| } |
| |
| @Override |
| protected void before() { |
| mySemaphore.down(); |
| } |
| |
| @Override |
| protected void callRunnable(Runnable runnable) { |
| myFuture = Executors.newSingleThreadExecutor().submit(runnable); |
| |
| final long tOut = (myTimeout < 20000) ? 20000 : myTimeout; |
| while (true) { |
| mySemaphore.waitFor(tOut); |
| if (myFuture.isDone() || myFuture.isCancelled()) break; |
| if (! commandStopper.isAlive()) break; |
| commandStopper.resetAlive(); |
| } |
| } |
| |
| @Override |
| protected void afterInRunnable() { |
| mySemaphore.up(); |
| } |
| |
| @Override |
| protected void after() throws CommandException { |
| if ((! myFuture.isDone() && (! myFuture.isCancelled()) && (! commandStopper.isAlive()))) { |
| myFuture.cancel(true); |
| throw new CommandException(new CommandAbortedException(), "Command execution timed out"); |
| } |
| } |
| } |
| |
| private class DirectProcessRequestHelper extends ProcessRequestsHelper { |
| @Override |
| protected void before() { |
| } |
| |
| @Override |
| protected void callRunnable(Runnable runnable) { |
| runnable.run(); |
| } |
| |
| @Override |
| protected void afterInRunnable() { |
| } |
| |
| @Override |
| protected void after() throws CommandException { |
| } |
| } |
| |
| private void sendRequests(Requests requests, IConnectionStreams connectionStreams, IRequestsProgressHandler communicationProgressHandler) |
| throws CommandAbortedException, IOException { |
| for (Iterator it = requests.getRequests().iterator(); it.hasNext();) { |
| final IRequest request = (IRequest)it.next(); |
| |
| sendRequest(request, connectionStreams); |
| |
| final FileDetails fileDetails = request.getFileForTransmission(); |
| if (fileDetails != null) { |
| sendFile(fileDetails, connectionStreams); |
| } |
| |
| communicationProgressHandler.requestSent(request); |
| } |
| } |
| |
| private void updateValidRequests(IConnectionStreams connectionStreams) throws CommandException, IOException { |
| sendRequest(new ValidRequestsRequest(), connectionStreams); |
| connectionStreams.flushForReading(); |
| handleResponses(connectionStreams, new ValidRequestsResponseHandler()); |
| |
| if (responseServices.getValidRequests() == null) { |
| throw new ValidRequestsExpectedException(); |
| } |
| } |
| |
| private void sendGlobalOptionRequests(IGlobalOptions globalOptions, IConnectionStreams connectionStreams) |
| throws CommandAbortedException, IOException { |
| if (!isValidRequest(GlobalOptionRequest.REQUEST)) { |
| return; |
| } |
| |
| if (globalOptions.isCheckedOutFilesReadOnly()) { |
| sendRequest(new GlobalOptionRequest("-r"), connectionStreams); |
| } |
| if (globalOptions.isDoNoChanges()) { |
| sendRequest(new GlobalOptionRequest("-n"), connectionStreams); |
| } |
| if (globalOptions.isNoHistoryLogging()) { |
| sendRequest(new GlobalOptionRequest("-l"), connectionStreams); |
| } |
| if (globalOptions.isSomeQuiet()) { |
| sendRequest(new GlobalOptionRequest("-q"), connectionStreams); |
| } |
| } |
| |
| private boolean isValidRequest(String request) { |
| return responseServices.getValidRequests().indexOf(request) >= 0; |
| } |
| |
| private void sendRequest(IRequest request, IConnectionStreams connectionStreams) throws CommandAbortedException, IOException { |
| checkCanceled(); |
| connectionStreams.getLoggedWriter().write(request.getRequestString()); |
| } |
| |
| private void checkCanceled() throws CommandAbortedException { |
| if (commandStopper.isAborted()) { |
| throw new CommandAbortedException(); |
| } |
| } |
| |
| private boolean handleResponses(IConnectionStreams connectionStreams, IResponseHandler responseHandler) |
| throws CommandException, IOException { |
| //final ErrorDefendingResponseHandler proxy = new ErrorDefendingResponseHandler(myTimeout, responseHandler); |
| |
| final ResponseParser responseParser = new ResponseParser(responseHandler, clientEnvironment.getCharset()); |
| final StringBuilder responseBuffer = new StringBuilder(32); |
| while (true) { |
| readResponse(connectionStreams.getLoggedReader(), responseBuffer); |
| checkCanceled(); |
| if (responseBuffer.length() == 0) { |
| return false; |
| } |
| |
| final Boolean result = |
| responseParser.processResponse(responseBuffer.toString(), connectionStreams, responseServices, clientEnvironment); |
| if (result != null) { |
| return result.booleanValue(); |
| } |
| |
| checkCanceled(); |
| /*if (proxy.interrupt()) { |
| throw new CommandException(null, "Aborted: consequent errors stream"); |
| }*/ |
| } |
| } |
| |
| private static void readResponse(Reader reader, StringBuilder responseBuffer) throws IOException { |
| responseBuffer.setLength(0); |
| |
| for (int chr = reader.read(); chr >= 0; chr = reader.read()) { |
| if (chr == '\n' || chr == ' ') { |
| break; |
| } |
| responseBuffer.append((char)chr); |
| } |
| } |
| |
| private void sendFile(FileDetails fileDetails, IConnectionStreams connectionStreams) throws IOException { |
| final FileObject fileObject = fileDetails.getFileObject(); |
| |
| if (fileDetails.isBinary()) { |
| clientEnvironment.getLocalFileReader().transmitBinaryFile(fileObject, connectionStreams, clientEnvironment.getCvsFileSystem()); |
| } |
| else { |
| clientEnvironment.getLocalFileReader().transmitTextFile(fileObject, connectionStreams, clientEnvironment.getCvsFileSystem()); |
| } |
| } |
| } |