/******************************************************************************* * Copyright (c) 2009 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 * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.debug.core.daemon; import java.io.IOException; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import org.eclipse.php.debug.daemon.communication.ICommunicationDaemon; import org.eclipse.php.internal.debug.core.Logger; /** * The debugger communication receiver holds a ServerSocket that remains open * for the entire Eclipse running session and accepts debug requests from remote * or local debuggers. * * @author Shalom Gibly * @since PDT 1.0 */ public abstract class AbstractDebuggerCommunicationDaemon implements ICommunicationDaemon { protected Object lock = new Object(); protected ServerSocket serverSocket; protected boolean isAlive; protected Thread listenerThread; private boolean isInitialized; /** * Constructs a new AbstractDebuggerCommunicationDaemon */ public AbstractDebuggerCommunicationDaemon() { } /** * Initializes the ServerSocket and starts a listen thread. Also, initialize * a preferences change listener for the port that is used by this daemon. */ public void init() { resetSocket(); } /** * Starts the listening thread for any incoming debug requests (responces). */ public void startListen() { synchronized (lock) { if (!isAlive && serverSocket != null) { startListenThread(); } else { isInitialized = true; } } } /** * Stops the listening thread. Any incoming request will not be treated. */ public void stopListen() { synchronized (lock) { isAlive = false; if (serverSocket != null) { try { if (!serverSocket.isClosed()) { serverSocket.close(); } } catch (SocketException se) { // do nothing in this case } catch (IOException e) { Logger.logException("Problem while closing the debugger ServerSocket.", //$NON-NLS-1$ e); } finally { serverSocket = null; } } } try { // Wait for the listener thread to die. // Wait, at most, 2 seconds. if (listenerThread != null) { listenerThread.join(2000); } } catch (InterruptedException e) { } } /** * Returns true if this daemon is listening for communication requests. * * @return True, if the daemon is listening; False, otherwise. */ public boolean isListening(int port) { synchronized (lock) { return isAlive && getReceiverPort() == port; } } /** * Initialize the ServerSocket to listen for debug requests on a specified * port. The port is defined in the workspace preferences. * * @return True, if the reset did not yield any errors; False, otherwise. */ public boolean resetSocket() { stopListen(); int port = getReceiverPort(); try { synchronized (lock) { serverSocket = new ServerSocket(port); startListen(); return true; } } catch (BindException exc) { handleMultipleBindingError(); } catch (IOException e) { Logger.logException("Error while restting the socket for the debug requests.", //$NON-NLS-1$ e); } return false; } /* * (non-Javadoc) * * @seeorg.eclipse.php.debug.daemon.communication.ICommunicationDaemon# * handleMultipleBindingError() */ public void handleMultipleBindingError() { final int port = getReceiverPort(); Logger.log(Logger.ERROR, "The debug port " //$NON-NLS-1$ + port + " is in use. Please select a different port for the debugger."); //$NON-NLS-1$ } /** * Returns the server socket port used for the debug requests listening * thread. * * @return The port specified in the preferences. */ public abstract int getReceiverPort(); /** * Starts a connection on the given Socket. This method should be overridden * by extending classes to create a different debug connections. * * @param socket */ protected abstract void startConnection(Socket socket); /** * Returns the debugger ID that is using this communication daemon. * * @return The debugger ID that is using this daemon. * @since PDT 1.0 */ public abstract String getDebuggerID(); /** * Starts the listening thread. If the thread is already started, nothing * should happen. */ protected void startListenThread() { synchronized (lock) { if (isAlive) { return; } isAlive = true; } String port = " - Port: " //$NON-NLS-1$ + ((serverSocket != null) ? String.valueOf(serverSocket.getLocalPort()) : "??"); //$NON-NLS-1$ listenerThread = new Thread(new ReceiverThread(), "PHP Debugger Daemon Thread " + port); //$NON-NLS-1$ listenerThread.setDaemon(true); listenerThread.start(); } public boolean isInitialized() { synchronized (lock) { return isInitialized; } } /* * The thread responsible of listening for debug requests. On every debug * request, a new thread of DebugConnectionThread is created and a debug * session is initialized. */ private class ReceiverThread implements Runnable { public void run() { isInitialized = true; try { while (isAlive) { Socket socket = serverSocket.accept(); socket.setReceiveBufferSize(1024 * 128); socket.setSendBufferSize(1024 * 128); startConnection(socket); } } catch (IOException e) { synchronized (lock) { if (isAlive) { Logger.logException( "Error while listening to incoming debug requests. Listen thread terminated!", //$NON-NLS-1$ e); isAlive = false; } } } } } /* * (non-Javadoc) * * @see * org.eclipse.php.internal.debug.daemon.communication.ICommunicationDaemon * #isEnabled() */ public boolean isEnabled() { return true; } }