/* * Tigase Jabber/XMPP Server * Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. Look for COPYING file in the top folder. * If not, see http://www.gnu.org/licenses/. * * $Rev$ * Last modified by $Author$ * $Date$ */ package tigase.server.ext; //~--- non-JDK imports -------------------------------------------------------- import tigase.db.comp.ComponentRepository; import tigase.net.ConnectionType; import tigase.net.SocketType; import tigase.server.ConnectionManager; import tigase.server.Packet; import tigase.server.ext.handlers.BindProcessor; import tigase.server.ext.handlers.ComponentAcceptStreamOpenHandler; import tigase.server.ext.handlers.ComponentConnectStreamOpenHandler; import tigase.server.ext.handlers.HandshakeProcessor; import tigase.server.ext.handlers.JabberClientStreamOpenHandler; import tigase.server.ext.handlers.SASLProcessor; import tigase.server.ext.handlers.StartTLSProcessor; import tigase.server.ext.handlers.StreamFeaturesProcessor; import tigase.server.ext.handlers.UnknownXMLNSStreamOpenHandler; import tigase.server.ext.lb.LoadBalancerIfc; import tigase.stats.StatisticsList; import tigase.util.TigaseStringprepException; import tigase.xml.Element; import tigase.xmpp.Authorization; import tigase.xmpp.PacketErrorTypeException; //~--- JDK imports ------------------------------------------------------------ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import javax.script.Bindings; //~--- classes ---------------------------------------------------------------- /** * Created: Sep 30, 2009 8:28:13 PM * * @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a> * @version $Rev$ */ public class ComponentProtocol extends ConnectionManager<ComponentIOService> implements ComponentProtocolHandler { /** * Variable <code>log</code> is a class logger. */ private static final Logger log = Logger.getLogger(ComponentProtocol.class.getName()); /** Field description */ public static final String EXTCOMP_REPO_CLASS_PROPERTY = "--extcomp-repo-class"; /** Field description */ public static final String EXTCOMP_REPO_CLASS_PROP_KEY = "repository-class"; /** Field description */ public static final String EXTCOMP_REPO_CLASS_PROP_VAL = "tigase.server.ext.CompDBRepository"; /** Field description */ public static final String EXTCOMP_BIND_HOSTNAMES = "--bind-ext-hostnames"; /** Field description */ public static final String PACK_ROUTED_KEY = "pack-routed"; /** Field description */ public static final String RETURN_SERVICE_DISCO_KEY = "service-disco"; /** Field description */ public static final boolean RETURN_SERVICE_DISCO_VAL = true; /** Field description */ public static final String IDENTITY_TYPE_KEY = "identity-type"; /** Field description */ public static final String IDENTITY_TYPE_VAL = "generic"; /** Field description */ public static final String CLOSE_ON_SEQUENCE_ERROR_PROP_KEY = "close-on-seq-error"; /** Field description */ public static final String MAX_AUTH_ATTEMPTS_PROP_KEY = "max-auth-attempts"; /** Field description */ public static final String AUTHENTICATION_TIMEOUT_PROP_KEY = "auth-timeout"; /** Field description */ public boolean PACK_ROUTED_VAL = false; // In seconds private long authenticationTimeOut = 15; /** * A map keeping all active connections by a connection JID or domain name. * Since for each domain we can have 1..N connections the Map value is a List * of connections. */ private Map<String, ArrayList<ComponentConnection>> connections = new ConcurrentHashMap<String, ArrayList<ComponentConnection>>(); private String[] hostnamesToBind = null; private int maxAuthenticationAttempts = 1; private ComponentRepository<CompRepoItem> repo = null; private Map<String, StreamOpenHandler> streamOpenHandlers = new LinkedHashMap<String, StreamOpenHandler>(); /** * List of processors which should handle all traffic incoming from the * network. In most cases if not all, these processors handle just protocol * traffic, all the rest traffic should be passed on to MR. */ private Map<String, ExtProcessor> processors = new LinkedHashMap<String, ExtProcessor>( 10); private UnknownXMLNSStreamOpenHandler unknownXMLNSHandler = new UnknownXMLNSStreamOpenHandler(); private String identity_type = IDENTITY_TYPE_VAL; // private ServiceEntity serviceEntity = null; private boolean closeOnSequenceError = true; private boolean experimental = false; /** * Constructs ... * */ public ComponentProtocol() { super(); StreamOpenHandler handler = new JabberClientStreamOpenHandler(); if (handler.getXMLNSs() != null) { for (String xmlns : handler.getXMLNSs()) { streamOpenHandlers.put(xmlns, handler); } } handler = new ComponentAcceptStreamOpenHandler(); if (handler.getXMLNSs() != null) { for (String xmlns : handler.getXMLNSs()) { streamOpenHandlers.put(xmlns, handler); } } handler = new ComponentConnectStreamOpenHandler(); if (handler.getXMLNSs() != null) { for (String xmlns : handler.getXMLNSs()) { streamOpenHandlers.put(xmlns, handler); } } } // ~--- methods -------------------------------------------------------------- /** * Method description * * * @param serv */ @Override public void authenticated(ComponentIOService serv) { serv.setAuthenticated(true); String hostname = (String) serv.getSessionData().get(ComponentIOService.HOSTNAME_KEY); bindHostname(hostname, serv); if (hostnamesToBind != null) { serv.getSessionData().put(EXTCOMP_BIND_HOSTNAMES_PROP_KEY, hostnamesToBind); ExtProcessor proc = getProcessor("bind"); if (proc != null) { Queue<Packet> results = new ArrayDeque<Packet>(); proc.startProcessing(null, serv, this, results); writePacketsToSocket(serv, results); } } } /** * Method description * * * @param serv * @param packet */ @Override public void authenticationFailed(ComponentIOService serv, Packet packet) { writePacketToSocket(serv, packet); Integer fails = (Integer) serv.getSessionData().get("auth-fails"); if (fails == null) { fails = 1; } else { fails += 1; } if (fails >= maxAuthenticationAttempts) { serv.stop(); } } /** * Method description * * * @param hostname * @param serv */ @Override public void bindHostname(String hostname, ComponentIOService serv) { String[] routings = new String[] { hostname, ".*@" + hostname, ".*\\." + hostname }; if (serv.connectionType() == ConnectionType.connect) { // Most likely we have an external component here which doesn't have any // connections managers. In such a case the best routings settings would // be: .* routings = new String[] { ".*" }; } serv.setRoutings(routings[0]); updateRoutings(routings, true); if (log.isLoggable(Level.FINE)) { log.fine("Authenticated: " + hostname); } updateServiceDiscoveryItem(hostname, null, "ext-comp connected", false); // Now kind of trick to allow access to the external component in a more // direct way. However, we have to careful here to avoid disaster. if (experimental) { updateServiceDiscoForConnection(hostname, serv); } addComponentConnection(hostname, serv); addComponentDomain(hostname); } private void updateServiceDiscoForConnection(String hostname, ComponentIOService serv) { // Cut off the first, component part int idx = hostname.indexOf("."); String newhostname = hostname.substring(idx + 1); if (!isLocalDomain(newhostname)) { updateServiceDiscoveryItem(newhostname, "ext", serv.getUniqueId(), true); } else { // We don't do the trick because this would break stuff } } /** * Method description * * * @param hostname * * @return */ @Override public CompRepoItem getCompRepoItem(String hostname) { return repo.getItem(hostname); } /** * Method description * * * @param params * * @return */ @Override @SuppressWarnings({ "unchecked" }) public Map<String, Object> getDefaults(Map<String, Object> params) { Map<String, Object> defs = super.getDefaults(params); experimental = Boolean.parseBoolean((String) params.get("--experimental")); String repo_class = (String) params.get(EXTCOMP_REPO_CLASS_PROPERTY); if (repo_class == null) { repo_class = EXTCOMP_REPO_CLASS_PROP_VAL; } defs.put(EXTCOMP_REPO_CLASS_PROP_KEY, repo_class); try { repo = (ComponentRepository<CompRepoItem>) Class.forName(repo_class).newInstance(); repo.getDefaults(defs, params); } catch (Exception e) { log.log(Level.SEVERE, "Can not instantiate items repository for class: " + repo_class, e); } defs.put(PACK_ROUTED_KEY, PACK_ROUTED_VAL); defs.put(RETURN_SERVICE_DISCO_KEY, RETURN_SERVICE_DISCO_VAL); defs.put(IDENTITY_TYPE_KEY, IDENTITY_TYPE_VAL); String bind_hostnames = (String) params.get(EXTCOMP_BIND_HOSTNAMES); if (bind_hostnames != null) { defs.put(EXTCOMP_BIND_HOSTNAMES_PROP_KEY, bind_hostnames.split(",")); } else { defs.put(EXTCOMP_BIND_HOSTNAMES_PROP_KEY, new String[] { "" }); } defs.put(CLOSE_ON_SEQUENCE_ERROR_PROP_KEY, closeOnSequenceError); defs.put(MAX_AUTH_ATTEMPTS_PROP_KEY, maxAuthenticationAttempts); defs.put(AUTHENTICATION_TIMEOUT_PROP_KEY, authenticationTimeOut); return defs; } /** * Method description * * * @return */ @Override public String getDiscoCategoryType() { return identity_type; } /** * Method description * * * @return */ @Override public String getDiscoDescription() { return "External component"; } /** * Method description * * * @param key * * @return */ @Override public ExtProcessor getProcessor(String key) { return processors.get(key); } /** * Method description * * * @param list */ @Override public void getStatistics(StatisticsList list) { super.getStatistics(list); // Warning size() for ConcurrentHashMap is very slow // unless we have a huge number of domains this should not be a problem // though. list.add(getName(), "Number of external domains", connections.size(), Level.FINE); int size = 0; for (ArrayList<ComponentConnection> conns : connections.values()) { size += conns.size(); } list.add(getName(), "Number of external component connections", size, Level.FINER); } /** * Method description * * * @param serv * * @return */ @Override public List<Element> getStreamFeatures(ComponentIOService serv) { List<Element> results = new LinkedList<Element>(); for (ExtProcessor proc : processors.values()) { List<Element> proc_res = proc.getStreamFeatures(serv, this); if (proc_res != null) { results.addAll(proc_res); } } return results; } /** * Method description * * * @param xmlns * * @return */ @Override public StreamOpenHandler getStreamOpenHandler(String xmlns) { return streamOpenHandlers.get(xmlns); } // ~--- methods -------------------------------------------------------------- /** * Method description * * * @param binds */ @Override public void initBindings(Bindings binds) { super.initBindings(binds); binds.put(ComponentRepository.COMP_REPO_BIND, repo); } /** * Method description * * * @param serv * * @return */ @Override public Queue<Packet> processSocketData(ComponentIOService serv) { Queue<Packet> packets = serv.getReceivedPackets(); Packet p = null; Queue<Packet> results = new ArrayDeque<Packet>(2); while ((p = packets.poll()) != null) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "Processing socket: {0}, data: {0}", new Object[] { serv, p }); } boolean processed = false; for (ExtProcessor proc : processors.values()) { processed |= proc.process(p, serv, this, results); writePacketsToSocket(serv, results); } if (!processed) { // This might be a bit slow, need to be tested. // Possibly a local variable in XMPPIOService might be needed // to improve performance if (serv.isAuthenticated()) { Packet result = p; if (p.isRouted()) { try { result = p.unpackRouted(); } catch (TigaseStringprepException ex) { log.log(Level.WARNING, "Packet stringprep addressing problem, dropping packet: {0}", p); return null; } } // end of if (p.isRouted()) result.getElement().setXMLNS("jabber:client"); if (result.getStanzaFrom() != null) { serv.addRecentJID(result.getStanzaFrom()); } addOutPacket(result); } else { try { Packet error = Authorization.NOT_AUTHORIZED.getResponseMessage(p, "Connection not yet authorized to send this packet.", true); writePacketToSocket(serv, error); } catch (PacketErrorTypeException ex) { // Already error packet, just ignore to prevent infinite loop log.log(Level.FINE, "Received an error packet from unauthorized connection: {0}", p); } if (closeOnSequenceError) { serv.stop(); } } } } // end of while () return null; } /** * Method description * * * @param port_props */ @Override public void reconnectionFailed(Map<String, Object> port_props) { // TODO: handle this somehow } /** * Method description * * * @param serv */ @Override public void serviceStarted(ComponentIOService serv) { super.serviceStarted(serv); addTimerTask(new AuthenticationTimer(serv), authenticationTimeOut, TimeUnit.SECONDS); String xmlns = ((CompRepoItem) serv.getSessionData().get(REPO_ITEM_KEY)).getXMLNS(); if (log.isLoggable(Level.FINEST)) { log.finest("Connection started: " + serv.getRemoteAddress() + ", xmlns: " + xmlns + ", type: " + serv.connectionType().toString() + ", id=" + serv.getUniqueId()); } StreamOpenHandler handler = streamOpenHandlers.get(xmlns); String result = null; if (handler == null) { // Well, that's a but, we should not be here... log.fine("XMLNS not set, accepting a new connection with xmlns auto-detection."); } else { if (log.isLoggable(Level.FINEST)) { log.finest("cid: " + (String) serv.getSessionData().get("cid") + ", sending: " + result); } result = handler.serviceStarted(serv); } if (result != null) { serv.xmppStreamOpen(result); } } /** * Method description * * * @param service * * @return */ @Override public boolean serviceStopped(ComponentIOService service) { boolean result = super.serviceStopped(service); if (result) { Map<String, Object> sessionData = service.getSessionData(); String hostname = (String) sessionData.get(ComponentIOService.HOSTNAME_KEY); if ((hostname != null) && !hostname.isEmpty()) { List<ComponentConnection> conns = service.getRefObject(); if (conns != null) { for (ComponentConnection conn : conns) { boolean moreConnections = removeComponentConnection(conn.getDomain(), conn); if (!moreConnections) { removeRoutings(conn.getDomain()); } } } else { // Nothing to do, let's log this however. log.finer("Closing XMPPIOService has not yet set ComponentConnection as RefObject: " + hostname + ", id: " + service.getUniqueId()); } } else { // Stopped service which hasn't sent initial stream open yet log.finer("Stopped service which hasn't sent initial stream open yet" + service.getUniqueId()); } ConnectionType type = service.connectionType(); if (type == ConnectionType.connect) { addWaitingTask(sessionData); // reconnectService(sessionData, connectionDelay); } // end of if (type == ConnectionType.connect) } return result; } // ~--- set methods ---------------------------------------------------------- /** * Method description * * * @param properties */ @Override @SuppressWarnings({ "unchecked" }) public void setProperties(Map<String, Object> properties) { if (properties.size() == 1) { // If props.size() == 1, it means this is a single property update // and this component does not support single property change for the rest // of it's settings return; } identity_type = (String) properties.get(IDENTITY_TYPE_KEY); super.setProperties(properties); String repo_class = (String) properties.get(EXTCOMP_REPO_CLASS_PROP_KEY); try { ComponentRepository<CompRepoItem> repo_tmp = (ComponentRepository<CompRepoItem>) Class.forName(repo_class).newInstance(); repo_tmp.setProperties(properties); repo = repo_tmp; } catch (Exception e) { log.log(Level.SEVERE, "Can not create items repository instance for class: " + repo_class, e); } // Activate all connections for which parameters are defined in the // repository for (CompRepoItem repoItem : repo) { log.config("Loaded repoItem: " + repoItem.toString()); if (repoItem.getPort() > 0) { String[] remote_host = PORT_IFC_PROP_VAL; String remote_domain = repoItem.getRemoteHost(); if (repoItem.getRemoteHost() != null) { remote_host = repoItem.getRemoteHost().split(";"); // The first item on the list is always the remote domain name, if // there are // more entries, the rest is just addresses to connect to for this // domain remote_domain = remote_host[0]; if (remote_host.length > 1) { // Remove the first entry as this is domain name, whereas the rest // is the // address to connect to. String[] remote_host_copy = new String[remote_host.length - 1]; System .arraycopy(remote_host, 1, remote_host_copy, 0, remote_host_copy.length); remote_host = remote_host_copy; } } for (String r_host : remote_host) { Map<String, Object> port_props = new LinkedHashMap<String, Object>(); port_props.put(PORT_KEY, repoItem.getPort()); if (repoItem.getDomain() != null) { port_props.put(PORT_LOCAL_HOST_PROP_KEY, repoItem.getDomain()); } port_props.put(PORT_REMOTE_HOST_PROP_KEY, remote_domain); port_props.put(PORT_TYPE_PROP_KEY, repoItem.getConnectionType()); port_props.put(PORT_SOCKET_PROP_KEY, SocketType.plain); port_props.put(PORT_IFC_PROP_KEY, new String[] { r_host }); port_props.put(MAX_RECONNECTS_PROP_KEY, (int) (120 * MINUTE)); port_props.put(REPO_ITEM_KEY, repoItem); log.config("Starting connection: " + port_props); addWaitingTask(port_props); } } } hostnamesToBind = (String[]) properties.get(EXTCOMP_BIND_HOSTNAMES_PROP_KEY); if ((hostnamesToBind.length == 1) && hostnamesToBind[0].isEmpty()) { hostnamesToBind = null; } log.config("Hostnames to bind: " + Arrays.toString(hostnamesToBind)); processors = new LinkedHashMap<String, ExtProcessor>(); ExtProcessor proc = new HandshakeProcessor(); processors.put(proc.getId(), proc); proc = new StreamFeaturesProcessor(); processors.put(proc.getId(), proc); proc = new StartTLSProcessor(); processors.put(proc.getId(), proc); proc = new SASLProcessor(); processors.put(proc.getId(), proc); proc = new BindProcessor(); processors.put(proc.getId(), proc); } /** * Method description * * * @param service */ @Override public void tlsHandshakeCompleted(ComponentIOService service) { } /** * Method description * * * @param hostname * @param serv */ @Override public void unbindHostname(String hostname, ComponentIOService serv) { ArrayList<ComponentConnection> conns = connections.get(hostname); if (conns != null) { ComponentConnection conn = null; for (ComponentConnection componentConnection : conns) { if (componentConnection.getService() == serv) { conn = componentConnection; } } if (conn != null) { boolean moreConnections = removeComponentConnection(conn.getDomain(), conn); if (!moreConnections) { removeRoutings(conn.getDomain()); } } } } /** * Method description * * * @param ios * @param p * * @return */ @Override public boolean writePacketToSocket(ComponentIOService ios, Packet p) { // String xmlns = (String)ios.getSessionData().get("xmlns"); // if (xmlns != null) { // p.getElement().setXMLNS(xmlns); // } p.getElement().removeAttribute("xmlns"); return super.writePacketToSocket(ios, p); } /** * Method description * * * @param serv */ @Override public void xmppStreamClosed(ComponentIOService serv) { } /** * Method description * * * @param serv * @param attribs * * @return */ @Override public String xmppStreamOpened(ComponentIOService serv, Map<String, String> attribs) { if (log.isLoggable(Level.FINEST)) { log.finest("Stream opened: " + serv.getRemoteAddress() + ", xmlns: " + attribs.get("xmlns") + ", type: " + serv.connectionType().toString() + ", uniqueId=" + serv.getUniqueId() + ", to=" + attribs.get("to")); } String s_xmlns = attribs.get("xmlns"); String result = null; StreamOpenHandler handler = streamOpenHandlers.get(s_xmlns); if ((handler == null) || (s_xmlns == null)) { log.finest("unknownXMLNSHandler is processing request"); result = unknownXMLNSHandler.streamOpened(serv, attribs, this); } else { log.finest(handler.getClass().getName() + " is processing request"); result = handler.streamOpened(serv, attribs, this); } if (log.isLoggable(Level.FINEST)) { log.finest("Sending back: " + result); } return result; } // ~--- get methods ---------------------------------------------------------- @Override protected long getMaxInactiveTime() { return 1000 * 24 * HOUR; } @Override protected Integer getMaxQueueSize(int def) { return def * 10; } @Override protected ComponentIOService getXMPPIOService(Packet p) { if (p.getStanzaTo() == null) { // This is a bad packet actually return null; } ComponentIOService result = null; String hostname = p.getStanzaTo().getDomain(); ArrayList<ComponentConnection> conns = connections.get(hostname); // If there is no connections list for this domain and routings are set to * // we use the first available list. for (ArrayList<ComponentConnection> c : connections.values()) { // Is there a better way to take the first available element? if (c.size() > 0 && ".*".equals(c.get(0).getService().getRoutings())) { conns = c; break; } } if (conns != null) { // First we check whether the receiver has sent to us a packet through one // of connections as this would be wise to send response on the same connection for (ComponentConnection componentConnection : conns) { ComponentIOService serv = componentConnection.getService(); if (serv != null && serv.isConnected() && serv.isRecentJID(p.getStanzaTo())) { result = serv; break; } } // Now, load balancer selects the best connection to send the packet if (result == null && conns.size() > 1) { CompRepoItem cmp_repo_item = getCompRepoItem(hostname); if (cmp_repo_item == null) { cmp_repo_item = repo.getItem(p.getStanzaFrom().getDomain()); } LoadBalancerIfc lb = cmp_repo_item.getLoadBalancer(); result = lb.selectConnection(p, conns); } // The above algorithm did not work for some reason. Now trying // traditional way to send a packet to the first available and working // connection if (result == null) { if (log.isLoggable(Level.FINEST)) { log.finest("LB could not select connection, trying traditional way"); } for (ComponentConnection componentConnection : conns) { ComponentIOService serv = componentConnection.getService(); if (serv != null) { if (serv.isConnected()) { result = serv; } else { log.info("Service is not connected for connection for hostname: " + hostname); } } else { log.info("Service is null for connection for hostname: " + hostname); } if (result != null) { break; } } } } else { log.info("No ext connection for hostname: " + hostname); } if (log.isLoggable(Level.FINEST)) { log.finest("Selected connection: " + result); } return result; } @Override protected ComponentIOService getXMPPIOServiceInstance() { return new ComponentIOService(); } @Override protected boolean isHighThroughput() { return true; } // ~--- methods -------------------------------------------------------------- private synchronized void addComponentConnection(String hostname, ComponentIOService s) { ComponentConnection conn = new ComponentConnection(hostname, s); List<ComponentConnection> refObject = s.getRefObject(); if (refObject == null) { refObject = new CopyOnWriteArrayList<ComponentConnection>(); s.setRefObject(refObject); } refObject.add(conn); ArrayList<ComponentConnection> conns = connections.get(hostname); if (conns == null) { conns = new ArrayList<ComponentConnection>(); connections.put(hostname, conns); } // Not very optimal, however this does not happen (should not) very often // and the data collections is optimized for fast object retrieval // by index (round robin balance for example) boolean result = conns.add(conn); if (result) { log.finer("A new component connection added for: " + hostname); } else { log.fine("A new component connection NOT added for: " + hostname); } } private synchronized boolean removeComponentConnection(String hostname, ComponentConnection conn) { boolean result = false; ArrayList<ComponentConnection> conns = connections.get(hostname); if (conns != null) { // This is slow, however this does not happen (should not) very often // and the data collections is optimized for fast object retrieval // by index (round robin balance for example) boolean removed = conns.remove(conn); if (removed) { log.finer("A component connection removed for: " + hostname); } else { log.fine("A component connection NOT removed for: " + hostname); } for (ComponentConnection compCon : conns) { ComponentIOService serv = compCon.getService(); if ((serv != null) && serv.isConnected()) { // There is still an active connection for this host result = true; } else { log.warning("Null or disconnected service for ComponentConnection for host: " + hostname); } } } else { log.warning("That should not happen, ComponentConnection is not null but " + "the collection is: " + hostname); } return result; } private void removeRoutings(String hostname) { String[] routings = new String[] { hostname, ".*@" + hostname, ".*\\." + hostname }; updateRoutings(routings, false); // removeRouting(serv.getRemoteHost()); // String addr = (String)sessionData.get(PORT_REMOTE_HOST_PROP_KEY); if (log.isLoggable(Level.FINE)) { log.fine("Disonnected from: " + hostname); } updateServiceDiscoveryItem(hostname, null, "XEP-0114 disconnected", false); removeComponentDomain(hostname); } private void updateRoutings(String[] routings, boolean add) { if (add) { for (String route : routings) { try { addRegexRouting(route); } catch (Exception e) { log.warning("Can not add regex routing '" + route + "' : " + e); } } } else { for (String route : routings) { try { removeRegexRouting(route); log.fine("Removed routings: " + route); } catch (Exception e) { log.warning("Can not remove regex routing '" + route + "' : " + e); } } } log.finest("All regex routings: " + getRegexRoutings().toString()); } // ~--- inner classes -------------------------------------------------------- private class AuthenticationTimer extends TimerTask { private ComponentIOService serv = null; // ~--- constructors ------------------------------------------------------- private AuthenticationTimer(ComponentIOService serv) { this.serv = serv; } // ~--- methods ------------------------------------------------------------ /** * Method description * */ @Override public void run() { if (!serv.isAuthenticated()) { serv.stop(); } } } } // ~ Formatted in Sun Code Convention // ~ Formatted by Jindent --- http://www.jindent.com