/* * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.gwt.dev.shell; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.dev.shell.BrowserChannelServer.SessionHandlerServer; import java.io.IOException; import java.net.BindException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; /** * Listens for connections from OOPHM clients. */ public class BrowserListener { /** * Get a query parameter to be added to the URL that specifies the address * of this listener. * * @param address address of host to use for connections * @param port TCP port number to use for connection * @return a query parameter */ public static String getDevModeURLParams(String address, int port) { return "gwt.codesvr=" + address + ":" + port; } private ServerSocket listenSocket; private Thread listenThread; private boolean ignoreRemoteDeath = false; /** * Listens for new connections from browsers. * * @param logger * @param port * @param handler */ public BrowserListener(final TreeLogger logger, String bindAddress, int port, final SessionHandlerServer handler) { try { listenSocket = new ServerSocket(); listenSocket.setReuseAddress(true); InetAddress address = InetAddress.getByName(bindAddress); listenSocket.bind(new InetSocketAddress(address, port)); if (logger.isLoggable(TreeLogger.TRACE)) { logger.log(TreeLogger.TRACE, "Started code server on port " + listenSocket.getLocalPort(), null); } listenThread = new Thread() { @Override public void run() { while (true) { try { Socket sock = listenSocket.accept(); TreeLogger branch = logger.branch(TreeLogger.TRACE, "Connection received from " + sock.getInetAddress().getCanonicalHostName() + ":" + sock.getPort()); try { sock.setTcpNoDelay(true); sock.setKeepAlive(true); } catch (SocketException e) { // Ignore non-critical errors. } BrowserChannelServer server = new BrowserChannelServer(branch, sock, handler, ignoreRemoteDeath); /* * This object is special-cased by the SessionHandler, used for * methods needed by the client like hasMethod/hasProperty/etc. * handler is used for this object just to make sure it doesn't * conflict with some real object exposed to the client. */ int id = server.getJavaObjectsExposedInBrowser().add(server); assert id == BrowserChannel.SPECIAL_SERVERMETHODS_OBJECT; } catch (IOException e) { logger.log(TreeLogger.ERROR, "Communications error", e); } } } }; listenThread.setName("Code server listener"); listenThread.setDaemon(true); } catch (BindException e) { logger.log(TreeLogger.ERROR, "Unable to bind socket on port " + port + " -- is another session active?", e); } catch (IOException e) { logger.log(TreeLogger.ERROR, "Communications error", e); } } /** * @return the port number of the listening socket. * * @throws UnableToCompleteException if the listener is not running */ public int getSocketPort() throws UnableToCompleteException { if (listenSocket == null) { // If we failed to initialize our socket, just bail here. throw new UnableToCompleteException(); } return listenSocket.getLocalPort(); } /** * Set any created BrowserChannelServers to ignore remote deaths. * * <p> * This is most commonly wanted by JUnitShell. * * @param ignoreRemoteDeath */ public void setIgnoreRemoteDeath(boolean ignoreRemoteDeath) { this.ignoreRemoteDeath = ignoreRemoteDeath; } /** * Start the listener thread. */ public void start() { if (listenThread != null) { listenThread.start(); } } }