| /******************************************************************************* |
| * Copyright (c) 2001, 2005 IBM Corporation 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 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jem.internal.proxy.vm.remote; |
| /* |
| |
| |
| */ |
| |
| |
| import java.util.*; |
| import java.io.*; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.net.*; |
| import org.eclipse.jem.internal.proxy.common.remote.*; |
| import org.eclipse.jem.internal.proxy.common.*; |
| /** |
| * RemoteVM Server Thread. This thread is the one |
| * that waits for connections and spins off |
| * server connection threads. It manages the |
| * connection threads and handles shutting them |
| * down. |
| * |
| * System Properties: |
| * proxyvm.port - Port number to use for the ServerSocket (default is 8888) |
| * proxyvm.bufsize - Buffer size to use for TCP/IP buffers (default is system default) |
| */ |
| |
| public class RemoteVMServerThread extends Thread implements IVMServer, IVMCallbackServer { |
| protected List threads = Collections.synchronizedList(new LinkedList()); // List of active threads. |
| protected ServerSocket server; // Server Socket for this application |
| private int highestIdentityID = 0; // Identity codes to identify objects between server and client. |
| private Map objectToIDMap; |
| private HashMap idToObjectMap = new HashMap(100); // Map from identity id to object |
| |
| protected Stack fCallbackHandlerPool = new Stack(); // Stack of free callback handlers |
| protected static int NUMBER_FREE_CALLBACKS = 5; // Number of free callback handlers to keep open. |
| |
| public static int ID_NOT_FOUND = Commands.NOT_AN_ID; // The id was not found in the table. |
| |
| protected int masterIDESocketPort = -1; // Port of master server socket on IDE. Used for special global requests. |
| protected int registryKey = -1; // Key of registry on the IDE. |
| |
| // Kludge: Bug in Linux 1.3.xxx of JVM. Closing a socket while the socket is being read/accept will not interrupt the |
| // wait. Need to timeout to the socket read/accept before the socket close will be noticed. This has been fixed |
| // in Linux 1.4. So on Linux 1.3 need to put timeouts in on those sockets that can be separately closed while reading/accepting. |
| static boolean LINUX_1_3 = "linux".equalsIgnoreCase(System.getProperty("os.name")) && System.getProperty("java.version","").startsWith("1.3"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ |
| |
| // If version 1.3.x, we need to use our IdentidyMap, if 1.4 or greater then we can use Java's IdentidyHashMap, which is more efficient than ours. |
| static Constructor IDENTIDYMAP_CLASS_CTOR; |
| static { |
| Class idClass; |
| try { |
| idClass = Class.forName("java.util.IdentityHashMap"); //$NON-NLS-1$ |
| } catch (ClassNotFoundException e) { |
| idClass = IdentityMap.class; |
| } |
| try { |
| IDENTIDYMAP_CLASS_CTOR = idClass.getConstructor(new Class[] {Integer.TYPE}); |
| } catch (SecurityException e) { |
| e.printStackTrace(); |
| } catch (NoSuchMethodException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| public RemoteVMServerThread(String name) { |
| super(name); |
| try { |
| objectToIDMap = (Map) IDENTIDYMAP_CLASS_CTOR.newInstance(new Object[] {new Integer(100)}); |
| } catch (IllegalArgumentException e) { |
| e.printStackTrace(); |
| } catch (InstantiationException e) { |
| e.printStackTrace(); |
| } catch (IllegalAccessException e) { |
| e.printStackTrace(); |
| } catch (InvocationTargetException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| // The purpose of this thread is to wait 5 minutes, then see if the IDE is still |
| // up. If it isn't it will go down. This is safety mechanism |
| // in case the client went down without cleaning up and telling the server to go down. |
| // That way it won't hang around forever. |
| private boolean goingDown = false; |
| private Thread safeClean = new Thread(new Runnable() { |
| public void run() { |
| while (!goingDown) { |
| if (Thread.interrupted()) |
| continue; // Get to clean uninterrupted state. |
| try { |
| Thread.sleep(5 * 60 * 1000); // Sleep five minutes |
| // Test if IDE still up. |
| if (!isAlive()) { |
| System.err.println("No registry available to connect with after five minutes. Shutting down."); //$NON-NLS-1$ |
| requestShutdown(); |
| break; |
| } |
| } catch (InterruptedException e) { |
| } |
| } |
| } |
| |
| /* |
| * See if still alive |
| */ |
| private boolean isAlive() { |
| Socket socket = getSocket(); |
| if (socket != null) { |
| try { |
| DataOutputStream out = new DataOutputStream(socket.getOutputStream()); |
| DataInputStream in = new DataInputStream(socket.getInputStream()); |
| |
| try { |
| out.writeByte(Commands.ALIVE); |
| out.writeInt(registryKey); |
| out.flush(); |
| return in.readBoolean(); |
| // Now get the result. |
| } finally { |
| try { |
| in.close(); |
| } catch (IOException e) { |
| } |
| try { |
| out.close(); |
| } catch (IOException e) { |
| } |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } finally { |
| try { |
| socket.close(); |
| } catch (IOException e) { |
| e.printStackTrace(); // They should be closing. If they aren't, then they accumulate and master server will start rejecting new ones. |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| }, "Timeout Termination Thread"); //$NON-NLS-1$ |
| |
| |
| public void run() { |
| |
| // Initialize the mapping table with certain pre-defined ids. |
| synchronized(objectToIDMap) { |
| objectToIDMap.put(Void.TYPE, new Integer(Commands.VOID_TYPE)); |
| idToObjectMap.put(new Integer(Commands.VOID_TYPE), Void.TYPE); |
| |
| objectToIDMap.put(Boolean.TYPE, new Integer(Commands.BOOLEAN_TYPE)); |
| idToObjectMap.put(new Integer(Commands.BOOLEAN_TYPE), Boolean.TYPE); |
| objectToIDMap.put(Boolean.class, new Integer(Commands.BOOLEAN_CLASS)); |
| idToObjectMap.put(new Integer(Commands.BOOLEAN_CLASS), Boolean.class); |
| |
| objectToIDMap.put(Integer.TYPE, new Integer(Commands.INTEGER_TYPE)); |
| idToObjectMap.put(new Integer(Commands.INTEGER_TYPE), Integer.TYPE); |
| objectToIDMap.put(Integer.class, new Integer(Commands.INTEGER_CLASS)); |
| idToObjectMap.put(new Integer(Commands.INTEGER_CLASS), Integer.class); |
| |
| objectToIDMap.put(Byte.TYPE, new Integer(Commands.BYTE_TYPE)); |
| idToObjectMap.put(new Integer(Commands.BYTE_TYPE), Byte.TYPE); |
| objectToIDMap.put(Byte.class, new Integer(Commands.BYTE_CLASS)); |
| idToObjectMap.put(new Integer(Commands.BYTE_CLASS), Byte.class); |
| |
| objectToIDMap.put(Short.TYPE, new Integer(Commands.SHORT_TYPE)); |
| idToObjectMap.put(new Integer(Commands.SHORT_TYPE), Short.TYPE); |
| objectToIDMap.put(Short.class, new Integer(Commands.SHORT_CLASS)); |
| idToObjectMap.put(new Integer(Commands.SHORT_CLASS), Short.class); |
| |
| objectToIDMap.put(Long.TYPE, new Integer(Commands.LONG_TYPE)); |
| idToObjectMap.put(new Integer(Commands.LONG_TYPE), Long.TYPE); |
| objectToIDMap.put(Long.class, new Integer(Commands.LONG_CLASS)); |
| idToObjectMap.put(new Integer(Commands.LONG_CLASS), Long.class); |
| |
| objectToIDMap.put(Character.TYPE, new Integer(Commands.CHARACTER_TYPE)); |
| idToObjectMap.put(new Integer(Commands.CHARACTER_TYPE), Character.TYPE); |
| objectToIDMap.put(Character.class, new Integer(Commands.CHARACTER_CLASS)); |
| idToObjectMap.put(new Integer(Commands.CHARACTER_CLASS), Character.class); |
| |
| objectToIDMap.put(Double.TYPE, new Integer(Commands.DOUBLE_TYPE)); |
| idToObjectMap.put(new Integer(Commands.DOUBLE_TYPE), Double.TYPE); |
| objectToIDMap.put(Double.class, new Integer(Commands.DOUBLE_CLASS)); |
| idToObjectMap.put(new Integer(Commands.DOUBLE_CLASS), Double.class); |
| |
| objectToIDMap.put(Float.TYPE, new Integer(Commands.FLOAT_TYPE)); |
| idToObjectMap.put(new Integer(Commands.FLOAT_TYPE), Float.TYPE); |
| objectToIDMap.put(Float.class, new Integer(Commands.FLOAT_CLASS)); |
| idToObjectMap.put(new Integer(Commands.FLOAT_CLASS), Float.class); |
| |
| objectToIDMap.put(String.class, new Integer(Commands.STRING_CLASS)); |
| idToObjectMap.put(new Integer(Commands.STRING_CLASS), String.class); |
| |
| objectToIDMap.put(java.math.BigDecimal.class, new Integer(Commands.BIG_DECIMAL_CLASS)); |
| idToObjectMap.put(new Integer(Commands.BIG_DECIMAL_CLASS), java.math.BigDecimal.class); |
| |
| objectToIDMap.put(java.math.BigInteger.class, new Integer(Commands.BIG_INTEGER_CLASS)); |
| idToObjectMap.put(new Integer(Commands.BIG_INTEGER_CLASS), java.math.BigInteger.class); |
| |
| objectToIDMap.put(Number.class, new Integer(Commands.NUMBER_CLASS)); |
| idToObjectMap.put(new Integer(Commands.NUMBER_CLASS), Number.class); |
| |
| objectToIDMap.put(Throwable.class, new Integer(Commands.THROWABLE_CLASS)); |
| idToObjectMap.put(new Integer(Commands.THROWABLE_CLASS), Throwable.class); |
| |
| |
| objectToIDMap.put(Object.class, new Integer(Commands.OBJECT_CLASS)); |
| idToObjectMap.put(new Integer(Commands.OBJECT_CLASS), Object.class); |
| |
| objectToIDMap.put(Class.class, new Integer(Commands.CLASS_CLASS)); |
| idToObjectMap.put(new Integer(Commands.CLASS_CLASS), Class.class); |
| |
| objectToIDMap.put(java.lang.reflect.AccessibleObject.class, new Integer(Commands.ACCESSIBLEOBJECT_CLASS)); |
| idToObjectMap.put(new Integer(Commands.ACCESSIBLEOBJECT_CLASS), java.lang.reflect.AccessibleObject.class); |
| |
| objectToIDMap.put(java.lang.reflect.Method.class, new Integer(Commands.METHOD_CLASS)); |
| idToObjectMap.put(new Integer(Commands.METHOD_CLASS), java.lang.reflect.Method.class); |
| |
| objectToIDMap.put(java.lang.reflect.Constructor.class, new Integer(Commands.CONSTRUCTOR_CLASS)); |
| idToObjectMap.put(new Integer(Commands.CONSTRUCTOR_CLASS), java.lang.reflect.Constructor.class); |
| |
| objectToIDMap.put(java.lang.reflect.Field.class, new Integer(Commands.FIELD_CLASS)); |
| idToObjectMap.put(new Integer(Commands.FIELD_CLASS), java.lang.reflect.Field.class); |
| |
| objectToIDMap.put(IVMServer.class, new Integer(Commands.IVMSERVER_CLASS)); |
| idToObjectMap.put(new Integer(Commands.IVMSERVER_CLASS), IVMServer.class); |
| |
| objectToIDMap.put(ICallback.class, new Integer(Commands.ICALLBACK_CLASS)); |
| idToObjectMap.put(new Integer(Commands.ICALLBACK_CLASS), ICallback.class); |
| |
| objectToIDMap.put(this, new Integer(Commands.REMOTESERVER_ID)); |
| idToObjectMap.put(new Integer(Commands.REMOTESERVER_ID), this); |
| |
| objectToIDMap.put(RemoteVMServerThread.class, new Integer(Commands.REMOTEVMSERVER_CLASS)); |
| idToObjectMap.put(new Integer(Commands.REMOTEVMSERVER_CLASS), RemoteVMServerThread.class); |
| |
| objectToIDMap.put(Thread.class, new Integer(Commands.THREAD_CLASS)); |
| idToObjectMap.put(new Integer(Commands.THREAD_CLASS), Thread.class); |
| |
| objectToIDMap.put(ExpressionProcesserController.class, new Integer(Commands.EXPRESSIONPROCESSERCONTROLLER_CLASS)); |
| idToObjectMap.put(new Integer(Commands.EXPRESSIONPROCESSERCONTROLLER_CLASS), ExpressionProcesserController.class); |
| |
| try { |
| java.lang.reflect.Method getMethod = Class.class.getMethod("getMethod", new Class[] {String.class, (new Class[0]).getClass()}); //$NON-NLS-1$ |
| objectToIDMap.put(getMethod, new Integer(Commands.GET_METHOD_ID)); |
| idToObjectMap.put(new Integer(Commands.GET_METHOD_ID), getMethod); |
| |
| java.lang.reflect.Method initMethod = ICallback.class.getMethod("initializeCallback", new Class[] {IVMCallbackServer.class, Integer.TYPE}); //$NON-NLS-1$ |
| objectToIDMap.put(initMethod, new Integer(Commands.INITIALIZECALLBACK_METHOD_ID)); |
| idToObjectMap.put(new Integer(Commands.INITIALIZECALLBACK_METHOD_ID), initMethod); |
| |
| } catch (NoSuchMethodException e) { |
| // Shouldn't really ever occur. |
| } |
| |
| highestIdentityID = Commands.FIRST_FREE_ID; |
| } |
| |
| masterIDESocketPort = Integer.getInteger("proxyvm.masterPort", -1).intValue(); //$NON-NLS-1$ |
| if (masterIDESocketPort == -1) { |
| // No ports specified, need to just shutdown. |
| shutdown(); |
| return; |
| } |
| |
| registryKey = Integer.getInteger("proxyvm.registryKey", -1).intValue(); //$NON-NLS-1$ |
| if (registryKey == -1) { |
| // No registry specified, need to just shutdown. |
| shutdown(); |
| return; |
| } |
| |
| safeClean.setPriority(Thread.MIN_PRIORITY); |
| safeClean.start(); |
| boolean trying = true; |
| try { |
| server = new ServerSocket(0, 50 , InetAddress.getByName("localhost")); //$NON-NLS-1$ |
| trying = false; |
| if (LINUX_1_3) |
| server.setSoTimeout(1000); // Linux 1.3 bug, see comment on LINUX_1_3 |
| if (registerServer(server.getLocalPort())) { |
| while(server != null) { |
| Socket incoming = null; |
| try { |
| incoming = server.accept(); |
| } catch (InterruptedIOException e) { |
| continue; // Timeout, try again |
| } catch (NullPointerException e) { |
| continue; // Server could of gone null after test in while, means shutting down. This probably would only happen Linux 1.3. |
| } |
| Thread st = new ConnectionThread(incoming, this, "Connection Thread"); //$NON-NLS-1$ |
| threads.add(st); |
| safeClean.interrupt(); // Let safeClean know there is a change |
| st.start(); |
| // Null out locals so they can be GC'd since this is a long running loop. |
| st = null; |
| incoming = null; |
| } |
| } |
| } catch (SocketException e) { |
| if (trying || server != null) |
| e.printStackTrace(); // Exception and not shutdown request, so print stack trace. |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| |
| // We've had an exception, either something really bad, or we were closed, |
| // so go through shutdowns. |
| shutdown(); |
| } |
| |
| |
| /** |
| * Get an identityID, return -1 if not found. |
| */ |
| public int getIdentityID(Object anObject) { |
| synchronized(objectToIDMap) { |
| Integer id = (Integer) objectToIDMap.get(anObject); |
| return id != null ? id.intValue() : ID_NOT_FOUND; |
| } |
| } |
| |
| /** |
| * Get an identityID and add it if not found. Place the id in the |
| * ValueObject passed in and return whether it was added (true) or was already in table (false) |
| */ |
| public boolean getIdentityID(Object anObject, Commands.ValueObject intoValue ) { |
| boolean added = false; |
| synchronized(objectToIDMap) { |
| Integer id = (Integer) objectToIDMap.get(anObject); |
| if (id == null) { |
| do { |
| if (++highestIdentityID == Commands.NOT_AN_ID) |
| ++highestIdentityID; // Don't let -1 be a valid id. |
| id = new Integer(highestIdentityID); |
| } while (idToObjectMap.containsKey(id)); // Make sure not in use, really shouldn't ever happen because we have over 4 billion before it wraps back |
| objectToIDMap.put(anObject, id); |
| idToObjectMap.put(id, anObject); |
| added = true; |
| } |
| intoValue.setObjectID(id.intValue()); |
| } |
| return added; |
| } |
| |
| /** |
| * Remove an identity object from the mapping. |
| */ |
| public void removeObject(Object anObject) { |
| synchronized(objectToIDMap) { |
| Integer id = (Integer) objectToIDMap.remove(anObject); |
| idToObjectMap.remove(id); |
| } |
| } |
| |
| /** |
| * Remove an identity object from the mapping, given the id. |
| */ |
| public void removeObject(int id) { |
| synchronized(objectToIDMap) { |
| Object o = idToObjectMap.remove(new Integer(id)); |
| objectToIDMap.remove(o); |
| } |
| } |
| |
| /** |
| * Get the object for an identity id |
| */ |
| public Object getObject(int id) { |
| synchronized(objectToIDMap) { |
| return idToObjectMap.get(new Integer(id)); |
| } |
| } |
| |
| /** |
| * Remove a thread from the list. |
| */ |
| public void removeConnectionThread(Thread thread) { |
| threads.remove(thread); |
| safeClean.interrupt(); // Let safe clean know there is a change. |
| } |
| |
| /** |
| * Use this to request a shutdown. If the server hasn't even been |
| * created yet, this will return false. |
| */ |
| public boolean requestShutdown() { |
| if (server == null) |
| return false; |
| // Closing the server socket should cause a break. |
| try { |
| ServerSocket srv = server; |
| server = null; // So that server knows it is being shutdown and not print exception msg. |
| srv.close(); |
| } catch (Exception e) { |
| } |
| return true; |
| } |
| |
| /** |
| * Request a callback stream to write to. |
| * When done, the stream should be closed to release the connection. |
| */ |
| public OutputStream requestStream(int callbackID, int msgID) throws CommandException { |
| CallbackHandler h = (CallbackHandler) getFreeCallbackHandler(); |
| if (h == null) |
| throw new CommandException("No callback handler retrieved.", null); //$NON-NLS-1$ |
| h.initiateCallbackStream(callbackID, msgID); |
| return new CallbackOutputStream(h, this); |
| } |
| |
| protected void shutdown() { |
| goingDown = true; |
| safeClean.interrupt(); // Let safeClean know to come down. |
| |
| if (server != null) |
| try { |
| server.close(); // Close it so that no more requests can be made. |
| } catch (Exception e) { |
| } |
| |
| // Go through each thread and ask it to close. Make a copy of the list so that we |
| // won't get into deadlocks. |
| ConnectionThread[] threadsArray = (ConnectionThread[]) threads.toArray(new ConnectionThread[0]); |
| for (int i=0; i<threadsArray.length; i++) { |
| // This is a harsh way to shut a connection down, but there's no |
| // other way I know of to interrupt the read on a socket. |
| threadsArray[i].close(); |
| } |
| |
| // Now that they've been told to close, wait on each one to finish. |
| for (int i=0; i<threadsArray.length; i++) |
| try { |
| threadsArray[i].join(10000); // Wait ten seconds, if longer, just go on to next one. |
| if (threadsArray[i].isAlive()) |
| System.out.println("*** Connection "+i+" did not die."); //$NON-NLS-1$ //$NON-NLS-2$ |
| } catch (InterruptedException e) { |
| } |
| |
| if (safeClean.isAlive()) { |
| try { |
| safeClean.join(10000); |
| if (safeClean.isAlive()) |
| System.out.println("*** Cleanup thread did not die."); //$NON-NLS-1$ |
| } catch (InterruptedException e) { |
| } |
| } |
| |
| // Now close the callbacks. |
| synchronized(fCallbackHandlerPool) { |
| // Now we walk through all of the free handlers and close them properly. |
| Iterator itr = fCallbackHandlerPool.iterator(); |
| while (itr.hasNext()) { |
| ((CallbackHandler) itr.next()).closeHandler(); |
| } |
| |
| fCallbackHandlerPool.clear(); |
| } |
| |
| List runnables = null; |
| synchronized (this) { |
| runnables = shutdownRunnables; |
| shutdownRunnables = null; |
| } |
| if (runnables != null) { |
| for (Iterator itr = runnables.iterator(); itr.hasNext();) { |
| try { |
| ((Runnable) itr.next()).run(); |
| } catch (RuntimeException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Return a free callback handler |
| */ |
| public ICallbackHandler getFreeCallbackHandler() { |
| synchronized(fCallbackHandlerPool) { |
| if (!fCallbackHandlerPool.isEmpty()) |
| return (ICallbackHandler) fCallbackHandlerPool.pop(); |
| // else we need to allocate one. |
| return createCallbackHandler(); |
| } |
| } |
| |
| /** |
| * Make a new callback handler |
| */ |
| protected ICallbackHandler createCallbackHandler() { |
| Socket callbackSocket = requestCallbackSocket(); |
| if (callbackSocket != null) { |
| CallbackHandler handler = new CallbackHandler(callbackSocket, this); |
| if (handler.isConnected()) |
| return handler; |
| |
| // Failed, close the socket. |
| try { |
| callbackSocket.close(); |
| } catch (IOException e) { |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Free the handler |
| */ |
| public void returnCallbackHandler(ICallbackHandler aHandler) { |
| CallbackHandler handler = (CallbackHandler) aHandler; |
| if (handler.isConnected()) |
| synchronized (fCallbackHandlerPool) { |
| if (fCallbackHandlerPool.size() < NUMBER_FREE_CALLBACKS) |
| fCallbackHandlerPool.push(handler); |
| else |
| handler.closeHandler(); // We don't need to maintain more than five free connections. |
| } |
| } |
| |
| /** |
| * Process a callback. |
| */ |
| public Object doCallback(ICallbackRunnable run) throws CommandException { |
| CallbackHandler handler = (CallbackHandler) getFreeCallbackHandler(); |
| if (handler != null) { |
| try { |
| try { |
| return run.run(handler); |
| } catch (CommandErrorException e) { |
| // This is command error, connection still good, don't retry, just pass it on. |
| // It means the other side said I processed it, but there is an error. |
| throw e; |
| } catch (CommandException e) { |
| if (!e.isRecoverable()) { |
| // Close this handler and try a new one, one more time. |
| handler.closeHandler(); |
| handler = (CallbackHandler) getFreeCallbackHandler(); |
| try { |
| return run.run(handler); |
| } catch (CommandException eAgain) { |
| // It failed again, just close the connection and rethrow the exception. |
| handler.closeHandler(); |
| throw eAgain; |
| } |
| } else |
| throw e; // Recoverable, rethrow exception. |
| } |
| } finally { |
| returnCallbackHandler(handler); |
| } |
| } else |
| throw new CommandException("No callback handler retrieved.", null); //$NON-NLS-1$ |
| |
| } |
| |
| /* |
| * Register the server. Return if ide still active |
| */ |
| private boolean registerServer(int vmserverPort) { |
| Socket socket = getSocket(); |
| if (socket != null) { |
| try { |
| DataOutputStream out = new DataOutputStream(socket.getOutputStream()); |
| DataInputStream in = new DataInputStream(socket.getInputStream()); |
| |
| try { |
| out.writeByte(Commands.REMOTE_STARTED); |
| out.writeInt(registryKey); |
| out.writeInt(vmserverPort); |
| out.flush(); |
| return in.readBoolean(); |
| } finally { |
| try { |
| in.close(); |
| } catch (IOException e) { |
| } |
| try { |
| out.close(); |
| } catch (IOException e) { |
| } |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } finally { |
| try { |
| socket.close(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| } |
| |
| return false; |
| } |
| |
| /* |
| * Request the callback socket. <code>null</code> if there isn't one. |
| */ |
| private Socket requestCallbackSocket() { |
| Socket socket = getSocket(); |
| if (socket != null) { |
| boolean closeSocket = true; |
| try { |
| DataOutputStream out = new DataOutputStream(socket.getOutputStream()); |
| DataInputStream in = new DataInputStream(socket.getInputStream()); |
| |
| try { |
| out.writeByte(Commands.ATTACH_CALLBACK); |
| out.writeInt(registryKey); |
| out.flush(); |
| closeSocket = !in.readBoolean(); |
| return !closeSocket ? socket : null; |
| } finally { |
| if (closeSocket) { |
| try { |
| in.close(); |
| } catch (IOException e) { |
| } |
| try { |
| out.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } finally { |
| if (closeSocket) { |
| try { |
| socket.close(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| |
| |
| protected Socket getSocket() { |
| // We are putting it off into a thread because there are no timeout capabilities on getting a socket. |
| // So we need to allow for that. |
| final Socket[] scArray = new Socket[1]; |
| final boolean[] waiting = new boolean[] {true}; |
| Thread doIt = new Thread(new Runnable() { |
| public void run() { |
| try { |
| Socket sc = new Socket("localhost", masterIDESocketPort); //$NON-NLS-1$ |
| synchronized (this) { |
| if (waiting[0]) |
| scArray[0] = sc; |
| else |
| sc.close(); // We are no longer waiting on this thread so close the socket since no one will use it. |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| }); |
| |
| doIt.start(); |
| while (true) { |
| try { |
| doIt.join(60000); |
| synchronized (doIt) { |
| waiting[0] = false; // To let it know we are no longer waiting |
| } |
| break; |
| } catch (InterruptedException e) { |
| } |
| } |
| |
| if (scArray[0] == null) { |
| System.out.println("Couldn't retrieve a socket from master server in 60 seconds."); //$NON-NLS-1$ |
| return null; // Couldn't get one, probably server is down. |
| } |
| |
| return scArray[0]; |
| } |
| |
| private List shutdownRunnables; |
| |
| public synchronized void addShutdownListener(Runnable runnable) { |
| if (shutdownRunnables == null) { |
| shutdownRunnables = new ArrayList(); |
| } else if (shutdownRunnables.contains(runnable)) |
| return; |
| shutdownRunnables.add(runnable); |
| } |
| |
| public synchronized void removeShutdownListener(Runnable runnable) { |
| if (shutdownRunnables != null) |
| shutdownRunnables.remove(runnable); |
| } |
| |
| |
| public IVMServer getIVMServer() { |
| return this; |
| } |
| } |