/** * Copyright (C) 2013 Alexander Szczuczko * * This file may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. */ package ca.szc.keratin.core.net; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import javax.net.SocketFactory; import javax.net.ssl.SSLSocketFactory; import net.engio.mbassy.bus.BusConfiguration; import org.pmw.tinylog.Logger; import ca.szc.keratin.core.net.handlers.BusErrorHandler; import ca.szc.keratin.core.net.handlers.DeadMessageHandler; import ca.szc.keratin.core.net.handlers.ServerPingHandler; import ca.szc.keratin.core.net.io.ConnectionThread; import ca.szc.keratin.core.net.io.OutputQueue; import ca.szc.keratin.core.net.mbassador.MBassadorWrapper; import ca.szc.keratin.core.net.util.InvalidPortException; import ca.szc.keratin.core.net.util.TrustAllTrustManager; public class IrcConnection { private final SocketFactory socketFactory; private InetSocketAddress endpoint; private MBassadorWrapper bus; private OutputQueue outputQueue; private ConnectionThread connectionThread; /** * Defines the different use states of SSL. See the javadoc of the members for more information. */ public enum SslMode { /** * Do not use SSL */ OFF, /** * Enable full SSL */ ON, /** * Enable SSL without host verification */ ON_NOHOST } /** * Create an IRC connection with a String address and SSL disabled. The connection is not active until connect is * called. * * @param address A string representing the address to connect to * @param port The port number within the valid range to connect to * @throws UnknownHostException If no IP address for the host could be found, or if a scope_id was specified for a * global IPv6 address. * @throws InvalidPortException If the port parameter is outside the specified range of valid port values. */ public IrcConnection( String address, int port ) throws UnknownHostException, InvalidPortException { this( InetAddress.getByName( address ), port, SslMode.OFF ); } /** * Create an IRC connection with SSL disabled. The connection is not active until connect is called. * * @param address A InetAddress representing the address to connect to * @param port The port number within the valid range to connect to * @throws InvalidPortException If the port parameter is outside the specified range of valid port values. */ public IrcConnection( InetAddress address, int port ) throws InvalidPortException { this( address, port, SslMode.OFF ); } /** * Create an IRC connection with a String address. The connection is not active until connect is called. * * @param address A string representing the address to connect to * @param port The port number within the valid range to connect to * @throws UnknownHostException If no IP address for the host could be found, or if a scope_id was specified for a * global IPv6 address. * @throws InvalidPortException If the port parameter is outside the specified range of valid port values. */ public IrcConnection( String address, int port, SslMode ssl ) throws UnknownHostException, InvalidPortException { this( InetAddress.getByName( address ), port, ssl ); } /** * Create an IRC connection. The connection is not active until connect is called. * * @param address A InetAddress representing the address to connect to * @param port The port number within the valid range to connect to * @throws InvalidPortException If the port parameter is outside the specified range of valid port values. */ public IrcConnection( InetAddress address, int port, SslMode ssl ) throws InvalidPortException { Logger.trace( "IrcConnection instantiation" ); try { endpoint = new InetSocketAddress( address, port ); } catch ( IllegalArgumentException e ) { throw new InvalidPortException( e ); } // Bus has to be made in this class's constructor because we want to be able to subscribe stuff to the bus // before connecting. BusConfiguration busConf = BusConfiguration.Default(); // busConf.setSubscriptionFactory( new TimeoutSubscriptionFactory() ); bus = new MBassadorWrapper( busConf ); if ( SslMode.ON.equals( ssl ) ) socketFactory = SSLSocketFactory.getDefault(); else if ( SslMode.ON_NOHOST.equals( ssl ) ) socketFactory = TrustAllTrustManager.getSSLSocketFactory(); else socketFactory = SocketFactory.getDefault(); } /** * Get the IrcEvent bus for the connection. May be called immediately. * * @return the central MBassador bus */ public MBassadorWrapper getEventBus() { return bus; } /** * Get the output Queue. May be called after calling {@link #connect()}. */ public OutputQueue getOutputQueue() { if (outputQueue != null) return outputQueue; else throw new IllegalStateException("OutputQueue cannot be requested before connection is made"); } /** * Activate the connection. Will return immediately after starting the connection thread. */ public void connect() { Logger.info( "Connecting" ); Logger.trace( "Subscribing to event bus" ); bus.addErrorHandler( new BusErrorHandler() ); bus.subscribe( new ServerPingHandler() ); bus.subscribe( new DeadMessageHandler() ); Logger.trace( "Creating/starting connection thread" ); connectionThread = new ConnectionThread( bus, endpoint, socketFactory ); outputQueue = connectionThread.getOutputQueue(); connectionThread.start(); Logger.trace( "Done set up" ); } /** * Deactivate the connection. */ public void disconnect() { Logger.info( "Disconnecting" ); Logger.trace( "Stopping worker thread" ); connectionThread.interrupt(); try { connectionThread.join(); } catch ( InterruptedException e ) { } Logger.trace( "Shutting down event bus" ); bus.shutdown(); bus = null; Logger.trace( "Done shut down" ); } }