📄 abstractlistener.java
字号:
package net.sf.dz.daemon.tcp.server;import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;import java.net.SocketException;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Set;import java.util.TreeSet;import javax.net.ssl.SSLException;import org.freehold.jukebox.conf.Configuration;import org.freehold.jukebox.logger.LogChannel;import org.freehold.jukebox.sem.SemaphoreGroup;import org.freehold.jukebox.service.ActiveService;import org.freehold.jukebox.service.PassiveService;import org.freehold.jukebox.service.ServiceUnavailableException;import net.sf.dz.util.HostHelper;import net.sf.dz.util.SSLContextFactory;import net.sf.dz.pnp.MulticastServer;import net.sf.dz.pnp.custom.SimpleBroadcastServer;import net.sf.dz.pnp.custom.SimpleMulticastServer;/** * The TCP connection listener. * * <p> * * Provides: * * <ul> * * <li> Configuration; * <li> Connection setup, including SSL; * <li> Client handling logic; * <li> Service announcements. * </ul> * * @author Copyright © <a href="mailto:vt@freehold.crocodile.org">Vadim Tkachenko</a> 2001-2004 * @version $Id: AbstractListener.java,v 1.2 2004/06/28 20:35:48 vtt Exp $ */abstract public class AbstractListener extends PassiveService { /** * Set of addresses to listen on. * * Empty set means that we're listening on all local addresses. */ private Set addressSet; /** * The port to listen to. */ private int port; /** * The port to broadcast on. */ private int broadcastPort; /** * Is the connection requested to be secure. */ private boolean secure; /** * Password for the key store. * * VT: Don't tell me it's insecure, just fix the implementation to be * more secure, all right? */ private String password; /** * The listener services. */ private Set listenerSet = new TreeSet(); /** * Set of active clients. */ private Set clientSet = new HashSet(); /** * Connection count. */ protected int connectionCount = 0; private MulticastServer multicastServer; abstract protected LogChannel getLogChannel(); public final Iterator iterator() { return clientSet.iterator(); } protected final void configure() { Configuration cf = getConfiguration(); String cfroot = getConfigurationRoot(); addressSet = new HashSet(cf.getList(cfroot + ".tcp.bind_address")); port = cf.getInteger(cfroot + ".tcp.port", getDefaultListenPort()); broadcastPort = cf.getInteger(cfroot + ".tcp.broadcast_port", getDefaultBroadcastPort()); // VT: NOTE: Current default is insecure - less hassle for initial // configuration secure = cf.getBoolean(cfroot + ".tcp.secure", false); if ( secure ) { password = cf.getString(cfroot + ".tcp.password"); } complain(LOG_INFO, getLogChannel(), "Binding to: " + addressSet); // Now, configure the subclass... configure2(); } abstract protected void configure2(); protected final void startup() throws Throwable { // Check if we're configured getConfiguration(); // Perform the startup actions for the subclasses first - it doesn't // make any sense to open the connection listener if the subclass // business logic determines that something's not right and we can't // start startup2(); // Let's see if we have any addresses configured. If we don't, it // means that we'll listen on all local ports. if ( addressSet.isEmpty() ) { complain(LOG_INFO, getLogChannel(), "Listening on all local addresses"); Listener l = new Listener(null, port); l.setLogger(getLogger()); if ( !l.start().waitFor() ) { complain(LOG_WARNING, getLogChannel(), "Failed to start listener on *"); } else { listenerSet.add(l); } } else { // Let's collect the list of existing network interfaces, their // addresses and see if we're properly configured. If there's a // mismatch, the bad address will be removed from addressSet. If it // turns out that the addressSet is empty after verification is // done, we refuse to start. Set validAddresses = HostHelper.getLocalAddresses(); for ( Iterator i = addressSet.iterator(); i.hasNext(); ) { String address = i.next().toString(); InetAddress configuredAddress = InetAddress.getByName(address); if ( !validAddresses.contains(configuredAddress) ) { complain(LOG_WARNING, getLogChannel(), "Address specified in the configuration is not locally present: " + address); i.remove(); continue; } Listener l = new Listener(InetAddress.getByName(address), port); l.setLogger(getLogger()); // VT: FIXME: It is possible to parallel the startups in order // to get all the listeners up faster, but this will complicate // the logic, so let's do it when it becomes *absolutely* // necessary. if ( !l.start().waitFor() ) { complain(LOG_WARNING, getLogChannel(), "Failed to start listener on '" + address + "'"); } else { listenerSet.add(l); } } } if ( listenerSet.isEmpty() ) { throw new IllegalStateException("No listeners could be started"); } // VT: FIXME: Make sure we include the server signature (bug // #914695). I'd guess that it should be passed down to us in the // constructor. multicastServer = new SimpleBroadcastServer(new HashSet(addressSet), broadcastPort); multicastServer.setLogger(getLogger()); multicastServer.announce(getAnnounce()); multicastServer.start(); } /** * Do a service specific startup. */ abstract protected void startup2() throws Throwable; /** * Provide a service signature. * * This signature must uniquely identify the module for the purpose of * broadcast announcement. */ abstract protected String getServiceSignature(); /** * Provide a reasonable default for the {@link #port port} to listen to. */ abstract protected int getDefaultListenPort(); /** * Provide a reasonable default for the {@link #port port} to broadcast on. */ abstract protected int getDefaultBroadcastPort(); /** * Syntax sugar to change an announce message. * * @param message Message to announce. */ protected final void announce(String message) { multicastServer.announce(message); } /** * Get the message to announce to our clients. */ protected synchronized String getAnnounce() { // The only things that make sense to include into *our* // announcement are: // - The server signature (FIXME); // - the port to connect to; // - whether the connection is expected to be secure. // If the client is able to receive the broadcast, then they will // get the broadcast source and use it as the other endpoint, and // this is sufficient. If they don't get the broadcast, they can't // see us anyway and therefore don't care about all the ports we're // listening at. return "/" + port + "/" + (secure ? "secure" : "insecure"); } protected void shutdown(Throwable cause) throws Throwable { complain(LOG_NOTICE, getLogChannel(), "Shutting down"); multicastServer.stop(); SemaphoreGroup stop = new SemaphoreGroup(); synchronized ( listenerSet ) { for ( Iterator i = listenerSet.iterator(); i.hasNext(); ) { Listener l = (Listener)i.next(); stop.add(l.stop()); i.remove(); } } complain(LOG_INFO, getLogChannel(), "Shut down listeners"); synchronized ( clientSet ) { for ( Iterator i = clientSet.iterator(); i.hasNext(); ) { ConnectionHandler ch = (ConnectionHandler)i.next(); ch.send("E Shutting down"); stop.add(ch.stop()); i.remove(); } } stop.waitForAll(); complain(LOG_INFO, getLogChannel(), "Shut down clients"); // Clean up the mess after the clients are gone cleanup(); // Now let's shut down the subclasses shutdown2(cause); } abstract protected void shutdown2(Throwable cause) throws Throwable; abstract protected ConnectionHandler createHandler(Socket socket, BufferedReader br, PrintWriter pw); /** * Find out whether more than one connection is allowed. * * @return false if multiple connections are allowed. */ abstract protected boolean isUnique(); /** * Clean up after the last active connection is gone. * * This method must be idempotent. */ abstract protected void cleanup() throws Throwable; protected final class Listener extends ActiveService implements Comparable { InetAddress addr; int port; ServerSocket ss; public Listener(InetAddress addr, int port) { this.addr = addr; this.port = port; } protected void startup() throws Throwable { try {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -