/* ******************************************************************************
* Copyright (c) 2006-2012 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
/**
*
*/
package org.xmind.core.command.transfer;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.osgi.util.NLS;
import org.xmind.core.command.Command;
import org.xmind.core.command.ICommand;
import org.xmind.core.command.IReturnValueConsumer;
import org.xmind.core.command.ReturnValue;
import org.xmind.core.command.arguments.Attributes;
import org.xmind.core.command.binary.IBinaryStore;
import org.xmind.core.internal.command.remote.Messages;
import org.xmind.core.internal.command.remote.RemoteCommandPlugin;
/**
* A basic handler that sends a command to a remote command service via an
* output stream and wait for responded return value from an input stream.
*
* @author Frank Shaka
*/
public class OutgoingCommandHandler {
private static final String DEBUG_OPTION = "/debug/outgoingCommandHandler"; //$NON-NLS-1$
private static boolean DEBUGGING = RemoteCommandPlugin.getDefault()
.isDebugging(DEBUG_OPTION);
private Object remoteLocation = "(unknown location)"; //$NON-NLS-1$
private String pluginId = RemoteCommandPlugin.PLUGIN_ID;
/**
*
*/
public OutgoingCommandHandler() {
}
/**
* For debugging purpose only.
*
* @param location
* the remote location representation
*/
public void setRemoteLocation(Object location) {
if (location == null)
location = "(unknown location)"; //$NON-NLS-1$
this.remoteLocation = location;
}
public Object getRemoteLocation() {
return remoteLocation;
}
public void setPluginId(String pluginId) {
if (pluginId == null)
pluginId = RemoteCommandPlugin.PLUGIN_ID;
this.pluginId = pluginId;
}
public String getPluginId() {
return pluginId;
}
public IStatus handleOutgoingCommand(IProgressMonitor monitor,
ICommand command, IReturnValueConsumer returnValueConsumer,
InputStream input, OutputStream output) {
ChunkReader reader = new ChunkReader(input);
try {
ChunkWriter writer = new ChunkWriter(output);
try {
return handleOutgoingCommand(monitor, command,
returnValueConsumer, reader, writer);
} finally {
try {
writer.close();
} catch (IOException e) {
RemoteCommandPlugin.log(null, e);
}
}
} finally {
try {
reader.close();
} catch (IOException e) {
RemoteCommandPlugin.log(null, e);
}
}
}
public IStatus handleOutgoingCommand(IProgressMonitor monitor,
ICommand command, IReturnValueConsumer returnValueConsumer,
ChunkReader reader, ChunkWriter writer) {
monitor.beginTask(null, 100);
monitor.subTask(Messages.OutgoingCommandHandler_ExcuteCommand);
IProgressMonitor executeMonitor = new SubProgressMonitor(monitor, 90);
IStatus returnValue = executeCommandRemotely(executeMonitor, command,
reader, writer);
if (!executeMonitor.isCanceled())
executeMonitor.done();
try {
monitor.subTask(Messages.OutgoingCommandHandler_ConsumeReturnValue);
IProgressMonitor consumeMonitor = new SubProgressMonitor(monitor,
10);
consumeCommand(consumeMonitor, command, returnValue,
returnValueConsumer);
if (!consumeMonitor.isCanceled())
consumeMonitor.done();
if (!monitor.isCanceled())
monitor.done();
return returnValue;
} finally {
if (returnValue != null && returnValue instanceof ReturnValue) {
Object value = ((ReturnValue) returnValue).getValue();
if (value instanceof IBinaryStore) {
((IBinaryStore) value).clear();
}
}
}
}
private IStatus executeCommandRemotely(IProgressMonitor monitor,
ICommand command, ChunkReader reader, ChunkWriter writer) {
monitor.beginTask(null, 100);
monitor.subTask(Messages.OutgoingCommandHandler_SendCommand);
IProgressMonitor sendMonitor = new SubProgressMonitor(monitor, 30);
try {
sendCommand(sendMonitor, command, writer);
} catch (Throwable e) {
return createSendingErrorStatus(e);
}
if (sendMonitor.isCanceled())
return Status.CANCEL_STATUS;
sendMonitor.done();
monitor.subTask(Messages.OutgoingCommandHandler_ReceiveReturnValue);
IProgressMonitor receiveMonitor = new SubProgressMonitor(monitor, 70);
IStatus returnValue;
try {
returnValue = receive(receiveMonitor, reader);
} catch (Throwable e) {
returnValue = createReceivingErrorStatus(e);
}
if (!receiveMonitor.isCanceled())
receiveMonitor.done();
if (!monitor.isCanceled())
monitor.done();
return returnValue;
}
private void sendCommand(IProgressMonitor monitor, ICommand command,
ChunkWriter writer) throws IOException {
monitor.beginTask(null, 100);
if (DEBUGGING)
System.out.println("Sending command to " //$NON-NLS-1$
+ remoteLocation + ": " + command); //$NON-NLS-1$
long start = System.currentTimeMillis();
String uri = Command.toURI(command);
writer.writeText(uri);
if (monitor.isCanceled())
return;
monitor.worked(40);
IBinaryStore files = command.getBinaryStore();
if (files != null && !files.isEmpty()) {
writer.writeText(CommandTransferUtil.MARKER_FILES);
if (monitor.isCanceled())
return;
monitor.worked(10);
IProgressMonitor filesMonitor = new SubProgressMonitor(monitor, 40);
CommandTransferUtil.writeFiles(filesMonitor, files, writer);
if (monitor.isCanceled())
return;
filesMonitor.done();
} else {
monitor.worked(50);
}
writer.writeText(""); //$NON-NLS-1$
if (monitor.isCanceled())
return;
monitor.worked(10);
writer.flush();
if (monitor.isCanceled())
return;
monitor.done();
long end = System.currentTimeMillis();
if (DEBUGGING)
System.out.println("Command sent to " //$NON-NLS-1$
+ remoteLocation + " (" //$NON-NLS-1$
+ (end - start) + " ms): " + command); //$NON-NLS-1$
}
private IStatus receive(IProgressMonitor monitor, ChunkReader reader)
throws IOException {
monitor.beginTask(null, 100);
if (DEBUGGING)
System.out.println("Receiving return value from " //$NON-NLS-1$
+ remoteLocation);
long start = System.currentTimeMillis();
String status = reader.readText();
if (status == null)
return null;
int severity;
try {
severity = Integer.parseInt(status, 10);
} catch (NumberFormatException e) {
return new Status(IStatus.ERROR, getPluginId(), NLS.bind(
Messages.OutgoingCommandHandler_InvalidReturnValueStatus,
status));
}
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
monitor.worked(15);
String pluginId = reader.readText();
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
monitor.worked(15);
String codeStr = reader.readText();
int code;
if (codeStr != null && !"".equals(codeStr)) { //$NON-NLS-1$
try {
code = Integer.parseInt(codeStr, 10);
} catch (NumberFormatException e) {
return new Status(IStatus.ERROR, getPluginId(), NLS.bind(
Messages.OutgoingCommandHandler_InvalidReturnValueCode,
codeStr));
}
} else {
code = 0;
}
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
monitor.worked(15);
String message = reader.readText();
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
monitor.worked(15);
String responseType = reader.readText();
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
monitor.worked(15);
IStatus returnValue;
if (CommandTransferUtil.MARKER_PROPERTIES.equals(responseType)) {
Attributes attrs = new Attributes();
String name;
String value;
while ((name = reader.readText()) != null
&& (value = reader.readText()) != null) {
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
if ("".equals(name) || "".equals(value)) //$NON-NLS-1$ //$NON-NLS-2$
break;
attrs.with(CommandTransferUtil.decode(name),
CommandTransferUtil.decode(value));
}
returnValue = new ReturnValue(severity, pluginId, code, message,
attrs);
} else if (CommandTransferUtil.MARKER_VALUES.equals(responseType)) {
List<String> values = new ArrayList<String>();
String value;
while ((value = reader.readText()) != null) {
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
if ("".equals(value)) //$NON-NLS-1$
break;
values.add(CommandTransferUtil.decode(value));
}
returnValue = new ReturnValue(severity, pluginId, code, message,
values.toArray(new String[values.size()]));
} else if (CommandTransferUtil.MARKER_FILES.equals(responseType)) {
IProgressMonitor filesMonitor = new SubProgressMonitor(monitor, 20);
IBinaryStore files = CommandTransferUtil.readFiles(filesMonitor,
reader);
if (filesMonitor.isCanceled()) {
if (files != null)
files.clear();
return Status.CANCEL_STATUS;
}
filesMonitor.done();
returnValue = new ReturnValue(severity, pluginId, code, message,
files);
} else {
returnValue = new Status(severity, pluginId, code, message, null);
}
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
monitor.done();
long end = System.currentTimeMillis();
if (DEBUGGING) {
System.out.println("Return value received from " //$NON-NLS-1$
+ remoteLocation + " (" //$NON-NLS-1$
+ (end - start) + " ms): " + returnValue); //$NON-NLS-1$
}
return returnValue;
}
private void consumeCommand(IProgressMonitor monitor, ICommand command,
IStatus returnValue, IReturnValueConsumer returnValueConsumer) {
if (returnValueConsumer != null) {
try {
IStatus consumed = returnValueConsumer.consumeReturnValue(
monitor, returnValue);
if (!monitor.isCanceled() && consumed != null
&& !consumed.isOK()
&& consumed.getSeverity() != IStatus.CANCEL) {
RemoteCommandPlugin.log(consumed);
}
} catch (Throwable e) {
RemoteCommandPlugin.log(
"Error occurred while consuming return value.", //$NON-NLS-1$
e);
}
}
}
protected IStatus createSendingErrorStatus(Throwable e) {
return new Status(IStatus.ERROR, getPluginId(), null, e);
}
protected IStatus createReceivingErrorStatus(Throwable e) {
if (e instanceof EOFException) {
return new Status(IStatus.WARNING, getPluginId(),
Messages.OutgoingCommandHandler_ConnectionClose, e);
}
return new Status(IStatus.ERROR, getPluginId(), null, e);
}
}