/** * Copyright 2009 Marc Stogaitis and Mimi Sun * * 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 org.gmote.server; import java.io.StreamCorruptedException; import java.net.BindException; import java.net.InetAddress; import java.net.Socket; import java.net.SocketException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JOptionPane; import org.gmote.common.DataReceiverIF; import org.gmote.common.PasswordProvider; import org.gmote.common.TcpConnection; import org.gmote.common.security.AuthenticationHandler; import org.gmote.server.media.MediaInfoUpdater; import org.gmote.server.visualtouchpad.VisualTouchpad; public class TcpConnectionHandler { private static final Logger LOGGER = Logger.getLogger(TcpConnectionHandler.class.getName()); private static final int MAX_OLD_SESSION_IDS = 5; private static TcpConnectionHandler instance = null; private static DataReceiverIF dataReceiver; private List<String> addressListeningOn = new ArrayList<String>(); private UpnpUtil upnpUtil = new UpnpUtil(); private TcpConnectionHandler() { } public static synchronized TcpConnectionHandler instance() { if (instance == null) { instance = new TcpConnectionHandler(); } return instance; } public static synchronized TcpConnectionHandler instance(DataReceiverIF newDataReceiver) { dataReceiver = newDataReceiver; if (instance == null) { instance = new TcpConnectionHandler(); } return instance; } public synchronized void addConnectionListener(InetAddress address) { addressListeningOn.add(address.getHostAddress().toLowerCase()); int port = upnpUtil.getPort(address); new Thread(new ConnectionReceiverThread(port)).start(); } public synchronized boolean isListeningOnAddress(InetAddress address) { return addressListeningOn.contains(address.getHostAddress().toLowerCase()); } public synchronized void listenOnAllIpAddresses() throws SocketException { for (InetAddress address : ServerUtil.findAllLocalIpAddresses(true)) { addConnectionListener(address); } } private class ConnectionReceiverThread implements Runnable { private int tcpPort; public ConnectionReceiverThread(int tcpPort) { this.tcpPort = tcpPort; } @Override public void run() { // Circular list that will contain the last 5 session ids. List<String> latestSessionIds = new ArrayList<String>(MAX_OLD_SESSION_IDS); PasswordProvider passProvider = new PasswordProvider() { public String fetchPassword() { return StringEncrypter.readPasswordFromFile(); } }; AuthenticationHandler authHandler = new AuthenticationHandler(GmoteServer.VERSION, GmoteServer.MINIMUM_CLIENT_VERSION); while (true) { TcpConnection con = null; try { LOGGER.info("Waiting for TCP connection on port: " + tcpPort); con = new TcpConnection(authHandler); Thread thread = con.listenForConnections(tcpPort, dataReceiver, passProvider); if (thread != null) { MediaInfoUpdater.instance().setClientConnection(con); MulticastServerThread.setConnectedClientIp(con.getConnectedClientAddress()); addToSessionList(con.getSessionId(), latestSessionIds); VisualTouchpad.instance().setConnection(con); } } catch (StreamCorruptedException e) { // This may be an HTTP request. Try to handle it. LOGGER .warning("Encountered a StreamCorruptedException: trying to handle it as an HTTP request"); Socket connectionSocket; if (con != null && (connectionSocket = con.getConnectionSocket()) != null) { GmoteHttpServer httpServer = new GmoteHttpServer(connectionSocket); httpServer.handleHttpRequestAsync(latestSessionIds); } else { LOGGER.log(Level.SEVERE, "Unable to handle StreamCorruptedException: " + e.getMessage(), e); } } catch (BindException e) { // The port is already in use. We'll exit. LOGGER.log(Level.SEVERE, e.getMessage(), e); String errorMessage = "Unable to use port: " + tcpPort + ". There may be an instance of" + " Gmote already running. Please close it and try again. For more help, please visit:\nhttp://www.gmote.org"; LOGGER.warning(errorMessage); JOptionPane.showMessageDialog(null, errorMessage); System.exit(1); } catch (Exception e) { // Catching all exceptions since this is the top layer of our app and // we'll try to recover from these exceptions. LOGGER.log(Level.SEVERE, e.getMessage(), e); } } } private void addToSessionList(String sessionId, List<String> latestSessionIds) { if (latestSessionIds.size() >= MAX_OLD_SESSION_IDS) { latestSessionIds.remove(0); } latestSessionIds.add(sessionId); } } }