package monolipse.core.launching; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.StringWriter; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; import monolipse.core.BooCore; import monolipse.core.IBooLaunchConfigurationConstants; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.*; public class ProcessMessenger { private final Object _socketMutex = new Object(); private Socket _socket; private final Map<String, IProcessMessageHandler> _handlers = new HashMap<String, IProcessMessageHandler>(); private ILaunchConfiguration _configuration; private int _timeout = 8000; public ProcessMessenger(ILaunchConfiguration configuration) { _configuration = configuration; } public void setTimeout(int timeout) { _timeout = timeout; } public int getTimeout() { return _timeout; } public synchronized void setMessageHandler(String messageName, IProcessMessageHandler handler) { if (null == handler) { _handlers.remove(messageName); } else { _handlers.put(messageName, handler); } } public void send(String name, String payload) throws IOException { if (null == name || null == payload) throw new IllegalArgumentException(); send(new ProcessMessage(name, payload)); } public void send(ProcessMessage message) throws IOException { synchronized (_socketMutex) { if (null == _socket) { launch(); try { // always wait a little longer // than the socket timeout _socketMutex.wait(_timeout*2); } catch (InterruptedException x) { BooCore.logException(x); throw new RuntimeException(x); } if (null == _socket) { throw new IOException("no connection from process"); } } doSend(message); } } private void doSend(ProcessMessage message) throws IOException { final BufferedWriter buffered = buffered(_socket.getOutputStream()); try { message.writeTo(buffered); } finally { buffered.flush(); } } public void unload() { synchronized (_socketMutex) { if (null == _socket) return; try { doSend(new ProcessMessage("QUIT", "")); try { _socketMutex.wait(500); } catch (InterruptedException e) { e.printStackTrace(); } } catch (IOException e) { BooCore.logException(e); } finally { if (null != _socket) { try { _socket.close(); } catch (IOException e) {} _socket = null; } } } } public void dispose() { try { unload(); } catch (Exception x) { BooCore.logException(x); } } private void launch() throws IOException { final String jobName = "ProcessMessenger [" + _configuration.getName() + "]"; final int portNumber = findAvailablePort(); final Job server = new Job(jobName) { protected IStatus run(IProgressMonitor monitor) { try { listen(monitor, portNumber); } catch (Exception x) { BooCore.logException(x); } return Status.OK_STATUS; } }; server.setPriority(Job.LONG); server.setSystem(true); server.schedule(); final Job client = new Job(jobName + " client") { protected IStatus run(IProgressMonitor monitor) { try { BooCore.logInfo(jobName + " using port " + Integer.toHexString(portNumber)); launchConfiguration(portNumber, monitor); } catch (Exception x) { BooCore.logException(x); } return Status.OK_STATUS; } private void launchConfiguration(int portNumber, IProgressMonitor monitor) throws CoreException { ILaunchConfigurationWorkingCopy workingCopy = _configuration.getWorkingCopy(); workingCopy.setAttribute(IBooLaunchConfigurationConstants.ATTR_PROCESS_MESSENGER_PORT, portNumber); workingCopy.launch("run", monitor); } }; client.schedule(); } private int findAvailablePort() { final int begin = 0x1B00; final int end = 0x1BFF; int i=begin; for (; i<=end; ++i) { if (isPortAvailable(i)) return i; } return i; } private static boolean isPortAvailable(int port) { try { new ServerSocket(port, 1, localhost()).close(); return true; } catch (Exception ioe) { } return false; } private void listen(IProgressMonitor monitor, int portNumber) { try { ServerSocket server = new ServerSocket(portNumber, 50, localhost()); server.setSoTimeout(_timeout); try { synchronized (_socketMutex) { _socket = server.accept(); _socketMutex.notifyAll(); } try { while (!monitor.isCanceled()) { ProcessMessage message = readMessage(monitor); if (null == message) break; if (message.name.equals("QUIT")) { synchronized (_socketMutex) { _socketMutex.notifyAll(); } break; } handle(message); } } finally { unload(); } } finally { server.close(); } } catch (IOException e) { BooCore.logException(e); } } private static InetAddress localhost() throws UnknownHostException { return InetAddress.getByName("127.0.0.1"); } private synchronized void handle(final ProcessMessage message) { final IProcessMessageHandler handler = _handlers.get(message.name); if (null == handler) return; SafeRunner.run(new ISafeRunnable() { public void handleException(Throwable exception) { BooCore.logException(exception); } public void run() throws Exception { handler.handle(message); } }); } private ProcessMessage readMessage(IProgressMonitor monitor) throws IOException { BufferedReader reader = buffered(_socket.getInputStream()); StringWriter buffer = new StringWriter(); PrintWriter writer = new PrintWriter(buffer); String name = reader.readLine(); while (true) { if (monitor.isCanceled()) return null; String line = reader.readLine(); if (null == line) return null; if (line.equals(ProcessMessage.END_MARKER)) break; if (line.endsWith(ProcessMessage.END_MARKER)) { writer.println(line.substring(0, line.length() - ProcessMessage.END_MARKER.length())); break; } writer.println(line); } return new ProcessMessage(name, buffer.getBuffer().toString()); } private BufferedWriter buffered(final OutputStream outputStream) { return new BufferedWriter(new OutputStreamWriter(outputStream)); } private BufferedReader buffered(final InputStream inputStream) { return new BufferedReader(new InputStreamReader(inputStream)); } }