diff --git a/build/lib/merge/jetty-continuation.jar b/build/lib/merge/jetty-continuation.jar new file mode 100644 index 0000000..c3daf40 --- /dev/null +++ b/build/lib/merge/jetty-continuation.jar Binary files differ diff --git a/build/lib/merge/jetty-http.jar b/build/lib/merge/jetty-http.jar new file mode 100644 index 0000000..6292246 --- /dev/null +++ b/build/lib/merge/jetty-http.jar Binary files differ diff --git a/build/lib/merge/jetty-io.jar b/build/lib/merge/jetty-io.jar new file mode 100644 index 0000000..254110a --- /dev/null +++ b/build/lib/merge/jetty-io.jar Binary files differ diff --git a/build/lib/merge/jetty-security.jar b/build/lib/merge/jetty-security.jar new file mode 100644 index 0000000..bc57bbd --- /dev/null +++ b/build/lib/merge/jetty-security.jar Binary files differ diff --git a/build/lib/merge/jetty-server.jar b/build/lib/merge/jetty-server.jar new file mode 100644 index 0000000..b4c59d1 --- /dev/null +++ b/build/lib/merge/jetty-server.jar Binary files differ diff --git a/build/lib/merge/jetty-servlet.jar b/build/lib/merge/jetty-servlet.jar new file mode 100644 index 0000000..b70d59d --- /dev/null +++ b/build/lib/merge/jetty-servlet.jar Binary files differ diff --git a/build/lib/merge/jetty-sslengine.jar b/build/lib/merge/jetty-sslengine.jar deleted file mode 100644 index b7404ea..0000000 --- a/build/lib/merge/jetty-sslengine.jar +++ /dev/null Binary files differ diff --git a/build/lib/merge/jetty-util.jar b/build/lib/merge/jetty-util.jar index a003573..29aa3a9 100644 --- a/build/lib/merge/jetty-util.jar +++ b/build/lib/merge/jetty-util.jar Binary files differ diff --git a/build/lib/merge/jetty-webapp.jar b/build/lib/merge/jetty-webapp.jar new file mode 100644 index 0000000..5b96cba --- /dev/null +++ b/build/lib/merge/jetty-webapp.jar Binary files differ diff --git a/build/lib/merge/jetty-xml.jar b/build/lib/merge/jetty-xml.jar new file mode 100644 index 0000000..38b6a13 --- /dev/null +++ b/build/lib/merge/jetty-xml.jar Binary files differ diff --git a/build/lib/merge/jetty.jar b/build/lib/merge/jetty.jar deleted file mode 100644 index 2622779..0000000 --- a/build/lib/merge/jetty.jar +++ /dev/null Binary files differ diff --git a/build/lib/versions.txt b/build/lib/versions.txt index 2e737f8..2f67b9f 100644 --- a/build/lib/versions.txt +++ b/build/lib/versions.txt @@ -8,9 +8,15 @@ commons-lang.jar | 2.3 dom4j.jar | 1.6.1 !jaxen.jar | 1.1 beta 4 (from DOM4J 1.6.1) -jetty.jar | Jetty 6.1.10 -jetty-sslengine.jar | Jetty 6.1.10 -jetty-util.jar | Jetty 6.1.10 +jetty-continuation.jar | Jetty 7.0.1.v20091125 +jetty-http.jar | Jetty 7.0.1.v20091125 +jetty-io.jar | Jetty 7.0.1.v20091125 +jetty-security.jar | Jetty 7.0.1.v20091125 +jetty-server.jar | Jetty 7.0.1.v20091125 +jetty-servlet.jar | Jetty 7.0.1.v20091125 +jetty-util.jar | Jetty 7.0.1.v20091125 +jetty-webapp.jar | Jetty 7.0.1.v20091125 +jetty-xml.jar | Jetty 7.0.1.v20091125 junit.jar | 3.8.1 jdic.jar | 0.9.1 (for windows only) jzlib.jar | 1.0.7 @@ -20,4 +26,4 @@ pack200task.jar | August 5, 2004 servlet.jar | Jetty 6.1.10 (2.5) xmltask.jar | 1.11 -xpp3.jar | XPP_3 1.1.4c \ No newline at end of file +xpp3.jar | XPP_3 1.1.4c diff --git a/src/bin/cmanager.sh b/src/bin/cmanager.sh index 9cca5b0..3bbbeae 100644 --- a/src/bin/cmanager.sh +++ b/src/bin/cmanager.sh @@ -62,7 +62,7 @@ #make it fully qualified CMANAGER_HOME=`cd "$CMANAGER_HOME" && pwd` fi -CMANAGER_OPTS="${CMANAGER_OPTS} -DmanagerHome=${CMANAGER_HOME}" +CMANAGER_OPTS="${CMANAGER_OPTS} -DmanagerHome=\"${CMANAGER_HOME}\"" # For Cygwin, ensure paths are in UNIX format before anything is touched @@ -75,7 +75,7 @@ #set the CMANAGER_LIB location CMANAGER_LIB="${CMANAGER_HOME}/lib" -CMANAGER_OPTS="${CMANAGER_OPTS} -Dcmanager.lib.dir=${CMANAGER_LIB}" +CMANAGER_OPTS="${CMANAGER_OPTS} -Dcmanager.lib.dir=\"${CMANAGER_LIB}\"" if [ -z "$JAVACMD" ] ; then diff --git a/src/java/org/jivesoftware/multiplexer/ConnectionWorkerThread.java b/src/java/org/jivesoftware/multiplexer/ConnectionWorkerThread.java index 7a59c06..f316d03 100644 --- a/src/java/org/jivesoftware/multiplexer/ConnectionWorkerThread.java +++ b/src/java/org/jivesoftware/multiplexer/ConnectionWorkerThread.java @@ -34,6 +34,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Iterator; +import java.util.List; import java.util.Random; /** @@ -142,19 +143,24 @@ } } else { - try { - // Get the real hostname to connect to using DNS lookup of the specified hostname - DNSUtil.HostAddress address = DNSUtil.resolveXMPPServerDomain(serverName, port); - realHostname = address.getHost(); - Log.debug("CM - Trying to connect to " + serverName + ":" + port + - "(DNS lookup: " + realHostname + ":" + port + ")"); - // Establish a TCP connection to the Receiving Server - socket.connect(new InetSocketAddress(realHostname, port), 20000); - Log.debug("CM - Plain connection to " + serverName + ":" + port + " successful"); + // Get the real hostname to connect to using DNS lookup of the specified hostname + List addresses = DNSUtil.resolveXMPPDomain(serverName, port); + for (Iterator it = addresses.iterator(); it.hasNext(); ) { + try { + realHostname = it.next().getHost(); + Log.debug("CM - Trying to connect to " + serverName + ":" + port + + "(DNS lookup: " + realHostname + ":" + port + ")"); + // Establish a TCP connection to the Receiving Server + socket.connect(new InetSocketAddress(realHostname, port), 20000); + Log.debug("CM - Plain connection to " + serverName + ":" + port + " successful"); + break; + } + catch (Exception e) { + Log.error("Error trying to connect to server: " + serverName + + "(DNS lookup: " + realHostname + ":" + port + ")", e); + } } - catch (Exception e) { - Log.error("Error trying to connect to server: " + serverName + - "(DNS lookup: " + realHostname + ":" + port + ")", e); + if (!socket.isConnected()) { return false; } } diff --git a/src/java/org/jivesoftware/multiplexer/net/DNSUtil.java b/src/java/org/jivesoftware/multiplexer/net/DNSUtil.java index 0c78759..abb6c89 100644 --- a/src/java/org/jivesoftware/multiplexer/net/DNSUtil.java +++ b/src/java/org/jivesoftware/multiplexer/net/DNSUtil.java @@ -14,11 +14,20 @@ import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.Log; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.Hashtable; +import java.util.List; import java.util.Map; import java.util.StringTokenizer; @@ -49,7 +58,7 @@ } } catch (Exception e) { - Log.error(e); + Log.error("Can't initialize DNS context!", e); } } @@ -69,47 +78,54 @@ * @param defaultPort default port to return if the DNS look up fails. * @return a HostAddress, which encompasses the hostname and port that the XMPP * server can be reached at for the specified domain. + * @deprecated replaced with support for multiple srv records, see + * {@link #resolveXMPPDomain(String, int)} */ + @Deprecated public static HostAddress resolveXMPPServerDomain(String domain, int defaultPort) { + return resolveXMPPDomain(domain, defaultPort).get(0); + } + + /** + * Returns a sorted list of host names and ports that the specified XMPP domain + * can be reached at for server-to-server communication. A DNS lookup for a SRV + * record in the form "_xmpp-server._tcp.example.com" is attempted, according + * to section 14.4 of RFC 3920. If that lookup fails, a lookup in the older form + * of "_jabber._tcp.example.com" is attempted since servers that implement an + * older version of the protocol may be listed using that notation. If that + * lookup fails as well, it's assumed that the XMPP server lives at the + * host resolved by a DNS lookup at the specified domain on the specified default port.

+ * + * As an example, a lookup for "example.com" may return "im.example.com:5269". + * + * @param domain the domain. + * @param defaultPort default port to return if the DNS look up fails. + * @return a list of HostAddresses, which encompasses the hostname and port that the XMPP + * server can be reached at for the specified domain. + */ + public static List resolveXMPPDomain(String domain, int defaultPort) { // Check if there is an entry in the internal DNS for the specified domain + List results = null; if (dnsOverride != null) { HostAddress hostAddress = dnsOverride.get(domain); if (hostAddress != null) { - return hostAddress; + results = new ArrayList(); + results.add(hostAddress); + return results; } } - if (context == null) { - return new HostAddress(domain, defaultPort); + + // Attempt the SRV lookup. + results = srvLookup("_xmpp-server._tcp." + domain); + if (results == null || results.isEmpty()) { + results = srvLookup("_jabber._tcp." + domain); } - String host = domain; - int port = defaultPort; - try { - Attributes dnsLookup = - context.getAttributes("_xmpp-server._tcp." + domain, new String[]{"SRV"}); - String srvRecord = (String)dnsLookup.get("SRV").get(); - String [] srvRecordEntries = srvRecord.split(" "); - port = Integer.parseInt(srvRecordEntries[srvRecordEntries.length-2]); - host = srvRecordEntries[srvRecordEntries.length-1]; + + // Use domain and default port as fallback. + if (results.isEmpty()) { + results.add(new HostAddress(domain, defaultPort)); } - catch (Exception e) { - // Attempt lookup with older "jabber" name. - try { - Attributes dnsLookup = - context.getAttributes("_jabber._tcp." + domain, new String[]{"SRV"}); - String srvRecord = (String)dnsLookup.get("SRV").get(); - String [] srvRecordEntries = srvRecord.split(" "); - port = Integer.parseInt(srvRecordEntries[srvRecordEntries.length-2]); - host = srvRecordEntries[srvRecordEntries.length-1]; - } - catch (Exception e2) { - // Do nothing - } - } - // Host entries in DNS should end with a ".". - if (host.endsWith(".")) { - host = host.substring(0, host.length()-1); - } - return new HostAddress(host, port); + return results; } /** @@ -163,16 +179,52 @@ return answer; } + private static List srvLookup(String lookup) { + if (lookup == null) { + throw new NullPointerException("DNS lookup can't be null"); + } + try { + Attributes dnsLookup = + context.getAttributes(lookup, new String[]{"SRV"}); + Attribute srvRecords = dnsLookup.get("SRV"); + if (srvRecords == null) { + Log.debug("No SRV record found for domain: " + lookup); + return new ArrayList(); + } + HostAddress[] hosts = new WeightedHostAddress[srvRecords.size()]; + for (int i = 0; i < srvRecords.size(); i++) { + hosts[i] = new WeightedHostAddress(((String)srvRecords.get(i)).split(" ")); + } + if (srvRecords.size() > 1) { + Arrays.sort(hosts, new SrvRecordWeightedPriorityComparator()); + } + return Arrays.asList(hosts); + } + catch (NameNotFoundException e) { + Log.debug("No SRV record found for: " + lookup, e); + } + catch (NamingException e) { + Log.error("Can't process DNS lookup!", e); + } + return new ArrayList(); + } + /** * Encapsulates a hostname and port. */ public static class HostAddress { - private String host; - private int port; + private final String host; + private final int port; private HostAddress(String host, int port) { - this.host = host; + // Host entries in DNS should end with a ".". + if (host.endsWith(".")) { + this.host = host.substring(0, host.length()-1); + } + else { + this.host = host; + } this.port = port; } @@ -198,4 +250,64 @@ return host + ":" + port; } } + + /** + * The representation of weighted address. + */ + public static class WeightedHostAddress extends HostAddress { + + private final int priority; + private final int weight; + + private WeightedHostAddress(String [] srvRecordEntries) { + super(srvRecordEntries[srvRecordEntries.length-1], + Integer.parseInt(srvRecordEntries[srvRecordEntries.length-2])); + weight = Integer.parseInt(srvRecordEntries[srvRecordEntries.length-3]); + priority = Integer.parseInt(srvRecordEntries[srvRecordEntries.length-4]); + } + + private WeightedHostAddress(String host, int port, int priority, int weight) { + super(host, port); + this.priority = priority; + this.weight = weight; + } + + /** + * Returns the priority. + * + * @return the priority. + */ + public int getPriority() { + return priority; + } + + /** + * Returns the weight. + * + * @return the weight. + */ + public int getWeight() { + return weight; + } + } + + /** + * A comparator for sorting multiple weighted host addresses according to RFC 2782. + */ + public static class SrvRecordWeightedPriorityComparator implements Comparator, Serializable { + private static final long serialVersionUID = -9207293572898848260L; + + public int compare(HostAddress o1, HostAddress o2) { + if (o1 instanceof WeightedHostAddress && o2 instanceof WeightedHostAddress) { + WeightedHostAddress srv1 = (WeightedHostAddress) o1; + WeightedHostAddress srv2 = (WeightedHostAddress) o2; + // 16 bit unsigned priority is more important as the 16 bit weight + return ((srv1.priority << 15) - (srv2.priority << 15)) + (srv2.weight - srv1.weight); + } + else { + // This shouldn't happen but if we don't have priorities we sort the addresses + return o1.toString().compareTo(o2.toString()); + } + } + } } \ No newline at end of file diff --git a/src/java/org/jivesoftware/multiplexer/net/http/HttpBindManager.java b/src/java/org/jivesoftware/multiplexer/net/http/HttpBindManager.java index 02b7c5a..4ee0003 100644 --- a/src/java/org/jivesoftware/multiplexer/net/http/HttpBindManager.java +++ b/src/java/org/jivesoftware/multiplexer/net/http/HttpBindManager.java @@ -12,19 +12,22 @@ package org.jivesoftware.multiplexer.net.http; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.HandlerCollection; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.webapp.WebAppContext; + import org.jivesoftware.util.*; import org.jivesoftware.multiplexer.net.SSLConfig; import org.jivesoftware.multiplexer.ConnectionManager; -import org.mortbay.jetty.Connector; -import org.mortbay.jetty.Handler; -import org.mortbay.jetty.Server; -import org.mortbay.jetty.handler.ContextHandler; -import org.mortbay.jetty.handler.ContextHandlerCollection; -import org.mortbay.jetty.handler.DefaultHandler; -import org.mortbay.jetty.nio.SelectChannelConnector; -import org.mortbay.jetty.security.SslSelectChannelConnector; -import org.mortbay.jetty.servlet.ServletHandler; -import org.mortbay.jetty.webapp.WebAppContext; import javax.net.ssl.SSLContext; import java.io.File; import java.security.KeyStore; @@ -69,7 +72,7 @@ private HttpBindManager() { // Configure Jetty logging to a more reasonable default. - System.setProperty("org.mortbay.log.class", "org.jivesoftware.util.log.util.JettyLog"); + System.setProperty("org.eclipse.jetty.util.log.class", "org.jivesoftware.util.log.util.JettyLog"); // JSP 2.0 uses commons-logging, so also override that implementation. System.setProperty("org.apache.commons.logging.LogFactory", "org.jivesoftware.util.log.util.CommonsLogFactory"); @@ -254,6 +257,10 @@ */ private synchronized void configureHttpBindServer(int port, int securePort) { httpBindServer = new Server(); + final QueuedThreadPool tp = new QueuedThreadPool(254); + tp.setName("Jetty-QTP-BOSH"); + httpBindServer.setThreadPool(tp); + Connector httpConnector = createConnector(port); Connector httpsConnector = createSSLConnector(securePort); if (httpConnector == null && httpsConnector == null) { @@ -271,13 +278,16 @@ createCrossDomainHandler(contexts, "/"); loadStaticDirectory(contexts); - httpBindServer.setHandlers(new Handler[]{contexts, new DefaultHandler()}); + HandlerCollection collection = new HandlerCollection(); + httpBindServer.setHandler(collection); + collection.setHandlers(new Handler[] { contexts, new DefaultHandler() }); } private void createBoshHandler(ContextHandlerCollection contexts, String boshPath) { ServletHandler handler = new ServletHandler(); handler.addServletWithMapping(HttpBindServlet.class, "/"); + handler.addFilterWithMapping(org.eclipse.jetty.continuation.ContinuationFilter.class,"/*",0); ContextHandler boshContextHandler = new ContextHandler(contexts, boshPath); boshContextHandler.setHandler(handler); } diff --git a/src/java/org/jivesoftware/multiplexer/net/http/HttpBindServlet.java b/src/java/org/jivesoftware/multiplexer/net/http/HttpBindServlet.java index ea5bcd4..c88442b 100644 --- a/src/java/org/jivesoftware/multiplexer/net/http/HttpBindServlet.java +++ b/src/java/org/jivesoftware/multiplexer/net/http/HttpBindServlet.java @@ -22,7 +22,7 @@ import org.dom4j.Element; import org.dom4j.DocumentHelper; import org.dom4j.QName; -import org.mortbay.util.ajax.ContinuationSupport; +import org.eclipse.jetty.continuation.ContinuationSupport; import org.apache.commons.lang.StringEscapeUtils; import javax.servlet.http.HttpServlet; @@ -262,7 +262,7 @@ } else { session.resetInactivityTimeout(); - connection.setContinuation(ContinuationSupport.getContinuation(request, connection)); + connection.setContinuation(ContinuationSupport.getContinuation(request)); request.setAttribute("request-session", connection.getSession()); request.setAttribute("request", connection.getRequestId()); try { diff --git a/src/java/org/jivesoftware/multiplexer/net/http/HttpConnection.java b/src/java/org/jivesoftware/multiplexer/net/http/HttpConnection.java index 177e808..9ec97cc 100644 --- a/src/java/org/jivesoftware/multiplexer/net/http/HttpConnection.java +++ b/src/java/org/jivesoftware/multiplexer/net/http/HttpConnection.java @@ -12,8 +12,8 @@ package org.jivesoftware.multiplexer.net.http; +import org.eclipse.jetty.continuation.Continuation; import org.jivesoftware.util.JiveConstants; -import org.mortbay.util.ajax.Continuation; /** * Represents one HTTP connection with a client using the HTTP Binding service. The client will wait @@ -32,6 +32,7 @@ private boolean isDelivered; private static final String CONNECTION_CLOSED = "connection closed"; + private static final String SUSPENDED = "org.eclipse.jetty.continuation.Suspended"; /** * Constructs an HTTP Connection. @@ -108,7 +109,7 @@ } if (continuation != null) { - continuation.setObject(body); + continuation.setAttribute("response-body", body); continuation.resume(); } else { @@ -117,8 +118,8 @@ } /** - * A call that will cause a wait, or in the case of Jetty the thread to be freed, if there is no - * deliverable currently available. Once the response becomes available, it is returned. + * A call that will suspend the request if there is no deliverable currently available. + * Once the response becomes available, it is returned. * * @return the deliverable to send to the client * @throws HttpBindTimeoutException to indicate that the maximum wait time requested by the @@ -172,8 +173,17 @@ } private String waitForResponse() throws HttpBindTimeoutException { - if (continuation.suspend(session.getWait() * JiveConstants.SECOND)) { - String deliverable = (String) continuation.getObject(); + // we enter this method when we have no messages pending delivery + // when we resume a suspended continuation, or when we time out + if (!Boolean.TRUE.equals(continuation.getAttribute(SUSPENDED))) { + continuation.setTimeout(session.getWait() * JiveConstants.SECOND); + continuation.suspend(); + continuation.setAttribute(SUSPENDED, Boolean.TRUE); + continuation.undispatch(); + } + + if (continuation.isResumed()) { + String deliverable = (String) continuation.getAttribute("response-body"); // This will occur when the hold attribute of a session has been exceded. this.isDelivered = true; if (deliverable == null) { @@ -184,6 +194,7 @@ } return deliverable; } + this.isDelivered = true; throw new HttpBindTimeoutException("Request " + requestId + " exceeded response time from " + "server of " + session.getWait() + " seconds."); diff --git a/src/java/org/jivesoftware/multiplexer/starter/JiveClassLoader.java b/src/java/org/jivesoftware/multiplexer/starter/JiveClassLoader.java index e794f70..1de6b31 100644 --- a/src/java/org/jivesoftware/multiplexer/starter/JiveClassLoader.java +++ b/src/java/org/jivesoftware/multiplexer/starter/JiveClassLoader.java @@ -39,7 +39,7 @@ * @throws java.net.MalformedURLException if the libDir path is not valid. */ JiveClassLoader(ClassLoader parent, File libDir) throws MalformedURLException { - super(new URL[] { libDir.toURL() }, parent); + super(new URL[] { libDir.toURI().toURL() }, parent); File[] jars = libDir.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { @@ -62,7 +62,7 @@ for (int i = 0; i < jars.length; i++) { if (jars[i].isFile()) { - addURL(jars[i].toURL()); + addURL(jars[i].toURI().toURL()); } } }