/*******************************************************************************
* Copyright (c) 2013 Rene Schneider, GEBIT Solutions GmbH 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
*******************************************************************************/
package de.gebit.integrity.remoting.client;
import java.io.IOException;
import java.io.Serializable;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import de.gebit.integrity.remoting.IntegrityRemotingConstants;
import de.gebit.integrity.remoting.transport.Endpoint;
import de.gebit.integrity.remoting.transport.EndpointListener;
import de.gebit.integrity.remoting.transport.MessageProcessor;
import de.gebit.integrity.remoting.transport.enums.BreakpointActions;
import de.gebit.integrity.remoting.transport.enums.ExecutionCommands;
import de.gebit.integrity.remoting.transport.enums.ExecutionStates;
import de.gebit.integrity.remoting.transport.messages.AbortExecutionMessage;
import de.gebit.integrity.remoting.transport.messages.AbstractMessage;
import de.gebit.integrity.remoting.transport.messages.BreakpointUpdateMessage;
import de.gebit.integrity.remoting.transport.messages.ExecutionControlMessage;
import de.gebit.integrity.remoting.transport.messages.ExecutionStateMessage;
import de.gebit.integrity.remoting.transport.messages.IntegrityRemotingVersionMessage;
import de.gebit.integrity.remoting.transport.messages.SetListBaselineMessage;
import de.gebit.integrity.remoting.transport.messages.SetListUpdateMessage;
import de.gebit.integrity.remoting.transport.messages.ShutdownRequestMessage;
import de.gebit.integrity.remoting.transport.messages.TestRunnerCallbackMessage;
import de.gebit.integrity.remoting.transport.messages.VariableUpdateMessage;
/**
* The remoting client.
*
* @author Rene Schneider - initial API and implementation
*
*/
public class IntegrityRemotingClient {
/**
* The endpoint used for the communication with the server.
*/
private Endpoint endpoint;
/**
* The listener.
*/
private IntegrityRemotingClientListener listener;
/**
* The current execution state.
*/
private ExecutionStates executionState;
/**
* Creates a new instance and connects to a given remoting host.
*
* @param aHost
* the host name or IP
* @param aPort
* the port
* @param aListener
* the listener
* @param aClassLoader
* the classloader to use when deserializing objects
* @throws UnknownHostException
* @throws IOException
*/
public IntegrityRemotingClient(String aHost, int aPort, IntegrityRemotingClientListener aListener,
ClassLoader aClassLoader) throws UnknownHostException, IOException {
if (aListener == null) {
throw new IllegalArgumentException("A listener must be provided.");
}
listener = aListener;
endpoint = new Endpoint(aHost, aPort, createProcessors(), new EndpointListener() {
@Override
public void onConnectionLost(Endpoint anEndpoint) {
executionState = null;
listener.onConnectionLost(anEndpoint);
}
@Override
public void onClosed(Endpoint anEndpoint) {
executionState = null;
}
}, aClassLoader);
endpoint.sendMessage(new IntegrityRemotingVersionMessage(IntegrityRemotingConstants.MAJOR_PROTOCOL_VERSION,
IntegrityRemotingConstants.MINOR_PROTOCOL_VERSION, IntegrityRemotingConstants.MAJOR_VERSION,
IntegrityRemotingConstants.MINOR_VERSION, IntegrityRemotingConstants.PATCH_VERSION,
IntegrityRemotingConstants.BUILD_VERSION));
}
/**
* Closes a given remoting session.
*/
public void close() {
if (isActive()) {
endpoint.close(false);
}
}
public boolean isActive() {
return (endpoint != null && endpoint.isActive());
}
/**
* Call this method to control the test execution on the server.
*
* @param aCommand
* the command to execute
*/
public void controlExecution(ExecutionCommands aCommand) {
sendMessage(new ExecutionControlMessage(aCommand));
}
public ExecutionStates getExecutionState() {
return executionState;
}
/**
* Requests an execution state update from the server.
*/
public void requestExecutionStateUpdate() {
sendMessage(new ExecutionStateMessage(null));
}
/**
* Creates a breakpoint at the specified entry reference.
*
* @param anEntryReference
*/
public void createBreakpoint(Integer anEntryReference) {
sendMessage(new BreakpointUpdateMessage(BreakpointActions.CREATE, anEntryReference));
}
/**
* Deletes a breakpoint.
*
* @param anEntryReference
* the entry at which the breakpoint shall be deleted
*/
public void deleteBreakpoint(Integer anEntryReference) {
sendMessage(new BreakpointUpdateMessage(BreakpointActions.REMOVE, anEntryReference));
}
/**
* Updates a variables' value on a fork.
*
* @param aName
* the fully qualified variable name
* @param aValue
* the new value
*/
public void updateVariableValue(String aName, Serializable aValue) {
sendMessage(new VariableUpdateMessage(aName, aValue));
}
/**
* Requests an immediate shutdown.
*/
public void requestShutdown() {
sendMessage(new ShutdownRequestMessage());
}
/**
* Sends a message to the server.
*
* @param aMessage
* the message to send
*/
protected void sendMessage(AbstractMessage aMessage) {
if (isActive()) {
endpoint.sendMessage(aMessage);
}
}
/**
* Creates processors to process messages.
*
* @return
*/
protected Map<Class<? extends AbstractMessage>, MessageProcessor<?>> createProcessors() {
Map<Class<? extends AbstractMessage>, MessageProcessor<?>> tempMap = new HashMap<Class<? extends AbstractMessage>, MessageProcessor<?>>();
tempMap.put(IntegrityRemotingVersionMessage.class, new MessageProcessor<IntegrityRemotingVersionMessage>() {
@Override
public void processMessage(IntegrityRemotingVersionMessage aVersion, Endpoint anEndpoint) {
if (IntegrityRemotingConstants.MAJOR_PROTOCOL_VERSION != aVersion.getProtocolMajorVersion()) {
listener.onVersionMismatch(aVersion, anEndpoint);
endpoint.close(false);
} else {
listener.onConnectionSuccessful(aVersion, anEndpoint);
}
}
});
tempMap.put(SetListBaselineMessage.class, new MessageProcessor<SetListBaselineMessage>() {
@Override
public void processMessage(SetListBaselineMessage aMessage, Endpoint anEndpoint) {
aMessage.getSetList().recreateTransientData();
listener.onBaselineReceived(aMessage.getSetList(), anEndpoint);
}
});
tempMap.put(ExecutionStateMessage.class, new MessageProcessor<ExecutionStateMessage>() {
@Override
public void processMessage(ExecutionStateMessage aMessage, Endpoint anEndpoint) {
executionState = aMessage.getState();
listener.onExecutionStateUpdate(aMessage.getState(), anEndpoint);
}
});
tempMap.put(SetListUpdateMessage.class, new MessageProcessor<SetListUpdateMessage>() {
@Override
public void processMessage(SetListUpdateMessage aMessage, Endpoint anEndpoint) {
listener.onSetListUpdate(aMessage.getUpdatedEntries(), aMessage.getEntryInExecution(), anEndpoint);
}
});
tempMap.put(BreakpointUpdateMessage.class, new MessageProcessor<BreakpointUpdateMessage>() {
@Override
public void processMessage(BreakpointUpdateMessage aMessage, Endpoint anEndpoint) {
switch (aMessage.getAction()) {
case CREATE:
listener.onConfirmCreateBreakpoint(aMessage.getEntryReference(), anEndpoint);
break;
case REMOVE:
listener.onConfirmRemoveBreakpoint(aMessage.getEntryReference(), anEndpoint);
break;
default:
break;
}
}
});
tempMap.put(TestRunnerCallbackMessage.class, new MessageProcessor<TestRunnerCallbackMessage>() {
@Override
public void processMessage(TestRunnerCallbackMessage aMessage, Endpoint anEndpoint) {
listener.onTestRunnerCallbackMessageRetrieval(aMessage.getCallbackClassName(),
aMessage.getCallbackMethod(), aMessage.getObjects());
}
});
tempMap.put(VariableUpdateMessage.class, new MessageProcessor<VariableUpdateMessage>() {
@Override
public void processMessage(VariableUpdateMessage aMessage, Endpoint anEndpoint) {
listener.onVariableUpdateRetrieval(aMessage.getName(), aMessage.getValue());
}
});
tempMap.put(AbortExecutionMessage.class, new MessageProcessor<AbortExecutionMessage>() {
@Override
public void processMessage(AbortExecutionMessage aMessage, Endpoint anEndpoint) {
listener.onAbortExecution(aMessage.getExceptionMessage(), aMessage.getExceptionStackTrace());
}
});
return tempMap;
}
}