/*******************************************************************************
* Copyright (c) 2009 the CHISEL group and contributors.
* 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
*
* Contributors:
* Del Myers - initial API and implementation
*******************************************************************************/
package ca.uvic.chisel.javasketch.launching.internal;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import ca.gc.drdc.oasis.tracing.cjvmtracer.internal.OasisCommand;
import ca.uvic.chisel.javasketch.FilterSettings;
import ca.uvic.chisel.javasketch.SketchPlugin;
import ca.uvic.chisel.javasketch.launching.LocalDataBaseTraceClient;
import ca.uvic.chisel.javasketch.launching.ui.FilterTab;
public class JavaAgentTraceClient extends LocalDataBaseTraceClient {
/**
*
*/
public static final String PROCESS_PROPERTIES_FILE = "process.properties";
/**
*
*/
public static final String ATTACH_TIME_PROPERTY = "time";
/**
*
*/
public static final String HOST_PROPERTY = "host";
/**
*
*/
public static final String PROCESS_PROPERTY = "process";
public static final String LABEL_PROPERTY = "label";
public static final String ID_PROPERTY = "id";
public static final String CONFIGURATION_FILE = "launch.configuration";
private Socket clientSocket;
private boolean connected;
private OutputStream commandOut;
private InputStream commandIn;
private ReadThread readThread;
private long terminationTime;
private class ReadThread extends Thread {
private volatile boolean done;
ReadThread() {
done = false;
}
@Override
public void run() {
while (!done) {
try {
OasisCommand cmd = OasisCommand.readExternal(commandIn);
switch (cmd.getCommand()) {
case OasisCommand.ACK_COMMAND:
//check to see if it is a pause or a restart
switch (cmd.getData()[0]) {
case OasisCommand.PAUSE_COMMAND:
handlePause();
break;
case OasisCommand.RESUME_COMMAND:
handleResume();
break;
}
break;
}
} catch (IOException e) {
done = true;
}
}
terminationTime = System.currentTimeMillis();
try {
clientSocket.close();
} catch (IOException e1) {}
JavaAgentTraceClient.this.finish();
}
protected void finish() {
done = true;
}
}
public JavaAgentTraceClient() {
readThread = new ReadThread();
terminationTime = -1;
}
@Override
public void doAttach(ILaunch launch, ILaunchConfiguration configuration, IProgressMonitor monitor)
throws CoreException {
int portNumber = configuration.getAttribute(JavaTraceConfigurationDelegate.TRACE_PORT, 0);
try {
for (int tries = 1; tries <= 5; tries++) {
//try to get the socket up to 5 times
try {
clientSocket = new Socket("localhost", portNumber);
//success
tries = 6;
} catch (IOException e) {
clientSocket = null;
if (tries > 5) {
//re-throw the exception so it gets caught outside
//this loop
throw e;
} else {
//wait a second for the java process to start
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
Thread.interrupted();
}
}
}
}
handshake(configuration);
readThread.start();
} catch (NullPointerException e) {
throw new CoreException(new Status(Status.ERROR, SketchPlugin.PLUGIN_ID, "No port to attach for process"));
} catch (UnknownHostException e) {
throw new CoreException(new Status(Status.ERROR, SketchPlugin.PLUGIN_ID, "Could not connect to host process", e));
} catch (IOException e) {
throw new CoreException(new Status(Status.ERROR, SketchPlugin.PLUGIN_ID, "Could not connect to host process", e));
}
}
private void handshake(ILaunchConfiguration config) throws IOException, CoreException {
commandOut = clientSocket.getOutputStream();
commandIn = new BufferedInputStream(clientSocket.getInputStream());
OasisCommand.newConnectCommand().writeExternal(commandOut);
// System.out.println("Before reading response");
//waitForAvailable(commandIn, 1000);
OasisCommand result = OasisCommand.readExternal(commandIn);
if (result == null) {
throw new IOException("Null response from server");
} else if (result.getCommand() != OasisCommand.ACK_COMMAND) {
throw new IOException("Expected Acknowledgement from server");
}
// System.out.println("Response: " + result.getDataString());
//
this.connected = true;
if (config.getAttribute(FilterTab.APPLY_AT_RUNTIME, false)) {
FilterSettings filters = FilterSettings.newSettings(config);
for (String filter : filters.getResolvedInclusionFilters()) {
applyAtRuntime(filter, false);
}
for (String filter : filters.getResolvedExclusionFilters()) {
applyAtRuntime(filter, true);
}
}
//send the begin command
OasisCommand.newStartCommand(getFileName()).writeExternal(commandOut);
//get the acknowledgement
//waitForAvailable(commandIn, 1000);
result = OasisCommand.readExternal(commandIn);
if (result == null) {
throw new IOException("Null response from server");
} else if (result.getCommand() != OasisCommand.ACK_COMMAND) {
throw new IOException("Expected Acknowledgement from server");
}
}
/**
* @param filter
* @throws IOException
*/
private void applyAtRuntime(String filter, boolean isExclusion) throws IOException {
OasisCommand result;
OasisCommand command = OasisCommand.newFilterCommand(filter, isExclusion);
if (command != null) {
command.writeExternal(commandOut);
result = OasisCommand.readExternal(commandIn);
if (result == null) {
throw new IOException("Null response from server");
} else if (result.getCommand() != OasisCommand.ACK_COMMAND) {
throw new IOException("Expected Acknowledgement from server");
}
}
}
@Override
public boolean canPauseTrace() {
return (connected);
}
@Override
protected void performPauseRequest() {
try {
if (canPauseTrace()) {
OasisCommand.newPauseCommand().writeExternal(commandOut);
}
} catch (IOException e) {};
}
@Override
protected boolean performResumeRequest() {
try {
if (canPauseTrace()) {
OasisCommand.newResumeCommand().writeExternal(commandOut);
}
} catch (IOException e) {};
return false;
}
@Override
public void sendEvent(Object event) throws IllegalArgumentException {
try {
OasisCommand command = (OasisCommand) event;
try {
command.writeExternal(commandOut);
} catch (IOException e) {}
} catch (ClassCastException e) {
throw new IllegalArgumentException(e);
}
}
@Override
public int getExitValue() throws DebugException {
return 0;
}
@SuppressWarnings({"rawtypes" })
@Override
public Object getAdapter(Class adapter) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean canTerminate() {
return (getLaunch() != null && clientSocket != null && !isTerminated());
}
@Override
public boolean isTerminated() {
return clientSocket.isClosed();
}
@Override
public void terminate() throws DebugException {
if (canTerminate()) {
try {
clientSocket.close();
readThread.finish();
} catch (IOException e) {
throw new DebugException(new Status(IStatus.ERROR, SketchPlugin.PLUGIN_ID, "Error terminating client.", e));
}
}
}
/* (non-Javadoc)
* @see ca.uvic.chisel.javasketch.launching.internal.ITraceClient#getTerminationTime()
*/
@Override
public long getTerminationTime() {
return terminationTime;
}
@Override
protected void doInitialize(ILaunchConfiguration configuration) throws CoreException {
}
}