diff --git a/build.gradle b/build.gradle index de25608b5..b0944e4c6 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,7 @@ allprojects { sonatypeStagingUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2' buildDate = (new java.text.SimpleDateFormat("yyyy-MM-dd")).format(new Date()) oneLineDesc = 'An Open Source XMPP (Jabber) client library' + jxmppVersion = "0.1.0" } group = 'org.igniterealtime.smack' sourceCompatibility = 1.7 @@ -35,7 +36,12 @@ allprojects { // first occurence of an dash with a dot. // For example 4.0.0-rc1 becomes 4.0.0.rc1, but // 4.0.0-SNAPSHOT-2014-05-01 becomes 4.0.0.SNAPSHOT-2014-05-01 - 'Bundle-Version': version.replaceFirst("-", ".")) + 'Bundle-Version': version.replaceFirst("-", "."), + 'Built-Date': new Date(), + 'Built-JDK': System.getProperty('java.version'), + 'Built-Gradle': gradle.gradleVersion, + 'Built-By': System.getProperty('user.name') + ) } eclipse { @@ -73,6 +79,9 @@ task javadocAll(type: Javadoc) { // Might need a classpath classpath = files(subprojects.collect {project -> project.sourceSets.main.compileClasspath}) + options.linkSource = true + options.use = true + options.links = ["http://docs.oracle.com/javase/$sourceCompatibility/docs/api/"] as String[] } import org.apache.tools.ant.filters.ReplaceTokens @@ -100,6 +109,9 @@ jar { enabled = false } +// Disable upload archives for the root project +uploadArchives.enabled = false + description = """\ Smack ${version} ${oneLineDesc}.""" @@ -157,7 +169,7 @@ subprojects { issueManagement { system 'JIRA' - url 'http://issues.igniterealtime.org/browse/SMACK' + url 'https://igniterealtime.org/issues/browse/SMACK' } distributionManagement { @@ -195,7 +207,9 @@ subprojects { rootProject.distributionZip { dependsOn build from(buildDir) { - include "$libsDirName/**" + include "$libsDirName/*${version}.jar" + include "$libsDirName/*${version}-javadoc.jar" + include "$libsDirName/*${version}-sources.jar" } } signing { @@ -204,7 +218,7 @@ subprojects { } } -['smack-extensions', 'smack-experimental', 'smack-legacy'].each { name -> +['smack-resolver-javax', 'smack-extensions', 'smack-experimental', 'smack-legacy'].each { name -> project(":$name") { jar { manifest { diff --git a/documentation/debugging.html b/documentation/debugging.html index c91d4b7a5..e2d1b126a 100644 --- a/documentation/debugging.html +++ b/documentation/debugging.html @@ -16,8 +16,8 @@ Debugging with Smack
Smack includes two built-in debugging consoles that will let you track all XML traffic between -the client and server. A lite debugger which is part of the smack.jar -and an enhanced debugger contained in smackx-debug.jar. +the client and server. A lite debugger which is part of the smack-core.jar +and an enhanced debugger contained in smack-debug.jar.
@@ -56,7 +56,7 @@ Smack uses the following logic to decide the debugger console to use:
diff --git a/documentation/extensions/intro.html b/documentation/extensions/intro.html index 811ba9174..244edb334 100644 --- a/documentation/extensions/intro.html +++ b/documentation/extensions/intro.html @@ -32,17 +32,17 @@
-JEP related: N/A -- this protocol is outdated now that the Multi-User Chat (MUC) JEP is available -(JEP-45). However, most +XEP related: N/A -- this protocol is outdated now that the Multi-User Chat (MUC) XEP is available +(XEP-45). However, most existing clients still use this older protocol. Once MUC support becomes more widespread, this API may be deprecated. diff --git a/documentation/extensions/messageevents.html b/documentation/extensions/messageevents.html index fd64fc2fe..4f03c7a6b 100644 --- a/documentation/extensions/messageevents.html +++ b/documentation/extensions/messageevents.html @@ -19,7 +19,7 @@ display, and composition of messages. There are three stages in this extension:<
Description
diff --git a/documentation/extensions/muc.html b/documentation/extensions/muc.html index f4e4877c8..54ce72fb0 100644 --- a/documentation/extensions/muc.html +++ b/documentation/extensions/muc.html @@ -22,7 +22,7 @@ Allows configuration of, participation in, and administration of individual text
-JEP related: JEP-49 +XEP related: XEP-49
-JEP related: JEP-90 +XEP related: XEP-90
Configuration
Smack has an initialization process that involves 2 phases.-Connection connection = new XMPPConnection("jabber.org"); +XMPPConnection connection = new XMPPTCPConnection("jabber.org"); connection.connect(); connection.login("mtucker", "password"); Chat chat = connection.getChatManager().createChat("jsmith@jivesoftware.com", new MessageListener() { diff --git a/resources/README.html b/resources/README.html index bd1895657..7d6e349e1 100644 --- a/resources/README.html +++ b/resources/README.html @@ -55,9 +55,9 @@ possible, instructions are provided for both Unix/Linux and Windows users.
About the Distribution
-The smack.jar file in the main distribution folder is the only binary file -required for embedding XMPP functionality into client applications. The optional +The smack-core.jar file in the main distribution folder. The optional smack-extensions.jar contains the Smack extensions while smack-debug.jar contains an enhanced debugger.
@@ -176,6 +175,7 @@ while smack-debug.jar contains an enhanced debugger.
View the changelog for a list of changes since the last release. +If you are upgrading from Smack 3 to Smack 4, then please consult the Smack 4 Readme and Upgrade Guide
License Agreements
+ * Note that not all XmlPullParser implementations will return a String on
+ * getText()
if the parser is on START_TAG or END_TAG. So you must not rely on this
+ * behavior when using the parser.
+ *
+ * The parser must be positioned on a START_TAG of an element which MUST NOT contain Mixed + * Content (as defined in XML 3.2.2), or else an XmlPullParserException will be thrown. + *
+ * This method is used for the parts where the XMPP specification requires elements that contain + * only text or are the empty element. + * + * @param parser + * @return the textual content of the element as String + * @throws XmlPullParserException + * @throws IOException + */ + public static String parseElementText(XmlPullParser parser) throws XmlPullParserException, IOException { + assert (parser.getEventType() == XmlPullParser.START_TAG); + String res; + if (parser.isEmptyElementTag()) { + res = ""; + } + else { + // Advance to the text of the Element + int event = parser.next(); + if (event != XmlPullParser.TEXT) { + throw new XmlPullParserException( + "Non-empty element tag not followed by text, while Mixed Content (XML 3.2.2) is disallowed"); + } + res = parser.getText(); + event = parser.next(); + if (event != XmlPullParser.END_TAG) { + throw new XmlPullParserException( + "Non-empty element tag contains child-elements, while Mixed Content (XML 3.2.2) is disallowed"); + } + } + return res; + } + + /** + * Returns the current element as string. + *+ * The parser must be positioned on START_TAG. + *
+ * Note that only the outermost namespace attributes ("xmlns") will be returned, not nested ones. + * + * @param parser the XML pull parser + * @return the element as string + * @throws XmlPullParserException + * @throws IOException + */ + public static String parseElement(XmlPullParser parser) throws XmlPullParserException, IOException { + assert(parser.getEventType() == XmlPullParser.START_TAG); + return parseContentDepth(parser, parser.getDepth()); + } + + /** + * Returns the content of a element as string. + *+ * The parser must be positioned on the START_TAG of the element which content is going to get + * returned. If the current element is the empty element, then the empty string is returned. If + * it is a element which contains just text, then just the text is returned. If it contains + * nested elements (and text), then everything from the current opening tag to the corresponding + * closing tag of the same depth is returned as String. + *
+ * Note that only the outermost namespace attributes ("xmlns") will be returned, not nested ones. * * @param parser the XML pull parser * @return the content of a tag as string * @throws XmlPullParserException if parser encounters invalid XML * @throws IOException if an IO error occurs */ - private static String parseContent(XmlPullParser parser) + public static String parseContent(XmlPullParser parser) throws XmlPullParserException, IOException { - int parserDepth = parser.getDepth(); - return parseContentDepth(parser, parserDepth); + assert(parser.getEventType() == XmlPullParser.START_TAG); + if (parser.isEmptyElementTag()) { + return ""; + } + // Advance the parser, since we want to parse the content of the current element + parser.next(); + return parseContentDepth(parser, parser.getDepth()); } + /** + * Returns the content from the current position of the parser up to the closing tag of the + * given depth. Note that only the outermost namespace attributes ("xmlns") will be returned, + * not nested ones. + *+ * This method is able to parse the content with MX- and KXmlParser. In order to achieve + * this some trade-off has to be make, because KXmlParser does not support xml-roundtrip (ie. + * return a String on getText() on START_TAG and END_TAG). We are therefore required to work + * around this limitation, which results in only partial support for XML namespaces ("xmlns"): + * Only the outermost namespace of elements will be included in the resulting String. + *
+ * + * @param parser + * @param depth + * @return the content of the current depth + * @throws XmlPullParserException + * @throws IOException + */ public static String parseContentDepth(XmlPullParser parser, int depth) throws XmlPullParserException, IOException { - StringBuilder content = new StringBuilder(); - while (!(parser.next() == XmlPullParser.END_TAG && parser.getDepth() == depth)) { - String text = parser.getText(); - if (text == null) { - throw new IllegalStateException("Parser should never return 'null' on getText() here"); + XmlStringBuilder xml = new XmlStringBuilder(); + int event = parser.getEventType(); + boolean isEmptyElement = false; + // XmlPullParser reports namespaces in nested elements even if *only* the outer ones defines + // it. This 'flag' ensures that when a namespace is set for an element, it won't be set again + // in a nested element. It's an ugly workaround that has the potential to break things. + String namespaceElement = null;; + while (true) { + if (event == XmlPullParser.START_TAG) { + xml.halfOpenElement(parser.getName()); + if (namespaceElement == null) { + String namespace = parser.getNamespace(); + if (StringUtils.isNotEmpty(namespace)) { + xml.attribute("xmlns", namespace); + namespaceElement = parser.getName(); + } + } + for (int i = 0; i < parser.getAttributeCount(); i++) { + xml.attribute(parser.getAttributeName(i), parser.getAttributeValue(i)); + } + if (parser.isEmptyElementTag()) { + xml.closeEmptyElement(); + isEmptyElement = true; + } + else { + xml.rightAngelBracket(); + } } - content.append(text); + else if (event == XmlPullParser.END_TAG) { + if (isEmptyElement) { + // Do nothing as the element was already closed, just reset the flag + isEmptyElement = false; + } + else { + xml.closeElement(parser.getName()); + } + if (namespaceElement != null && namespaceElement.equals(parser.getName())) { + // We are on the closing tag, which defined the namespace as starting tag, reset the 'flag' + namespaceElement = null; + } + if (parser.getDepth() <= depth) { + // Abort parsing, we are done + break; + } + } + else if (event == XmlPullParser.TEXT) { + xml.append(parser.getText()); + } + event = parser.next(); } - return content.toString(); + return xml.toString(); } /** diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java index 7c6bf6c3c..ff828f648 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java @@ -174,6 +174,25 @@ public class XmlStringBuilder implements Appendable, CharSequence { return this; } + public XmlStringBuilder emptyElement(String element) { + halfOpenElement(element); + return closeEmptyElement(); + } + + public XmlStringBuilder condEmptyElement(boolean condition, String element) { + if (condition) { + emptyElement(element); + } + return this; + } + + public XmlStringBuilder condAttribute(boolean condition, String name, String value) { + if (condition) { + attribute(name, value); + } + return this; + } + @Override public XmlStringBuilder append(CharSequence csq) { assert csq != null; @@ -213,4 +232,18 @@ public class XmlStringBuilder implements Appendable, CharSequence { public String toString() { return sb.toString(); } + + @Override + public boolean equals(Object other) { + if (!(other instanceof XmlStringBuilder)) { + return false; + } + XmlStringBuilder otherXmlStringBuilder = (XmlStringBuilder) other; + return toString().equals(otherXmlStringBuilder.toString()); + } + + @Override + public int hashCode() { + return toString().hashCode(); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/dns/HostAddress.java b/smack-core/src/main/java/org/jivesoftware/smack/util/dns/HostAddress.java index f051805c4..9b34239e5 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/dns/HostAddress.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/dns/HostAddress.java @@ -1,6 +1,6 @@ /** * - * Copyright 2013 Florian Schmaus + * Copyright © 2013-2014 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,11 @@ */ package org.jivesoftware.smack.util.dns; +import org.jivesoftware.smack.SmackException.ConnectionException; + public class HostAddress { - private String fqdn; - private int port; + private final String fqdn; + private final int port; private Exception exception; /** @@ -28,16 +30,8 @@ public class HostAddress { * @throws IllegalArgumentException If the fqdn is null. */ public HostAddress(String fqdn) { - if (fqdn == null) - throw new IllegalArgumentException("FQDN is null"); - if (fqdn.charAt(fqdn.length() - 1) == '.') { - this.fqdn = fqdn.substring(0, fqdn.length() - 1); - } - else { - this.fqdn = fqdn; - } // Set port to the default port for XMPP client communication - this.port = 5222; + this(fqdn, 5222); } /** @@ -48,11 +42,17 @@ public class HostAddress { * @throws IllegalArgumentException If the fqdn is null or port is out of valid range (0 - 65535). */ public HostAddress(String fqdn, int port) { - this(fqdn); + if (fqdn == null) + throw new IllegalArgumentException("FQDN is null"); if (port < 0 || port > 65535) throw new IllegalArgumentException( "Port must be a 16-bit unsiged integer (i.e. between 0-65535. Port was: " + port); - + if (fqdn.charAt(fqdn.length() - 1) == '.') { + this.fqdn = fqdn.substring(0, fqdn.length() - 1); + } + else { + this.fqdn = fqdn; + } this.port = port; } @@ -68,6 +68,17 @@ public class HostAddress { this.exception = e; } + /** + * Retrieve the Exception that caused a connection failure to this HostAddress. Every + * HostAddress found in {@link ConnectionException} will have an Exception set, + * which can be retrieved with this method. + * + * @return the Exception causing this HostAddress to fail + */ + public Exception getException() { + return this.exception; + } + @Override public String toString() { return fqdn + ":" + port; diff --git a/smack-core/src/test/java/org/jivesoftware/smack/RosterTest.java b/smack-core/src/test/java/org/jivesoftware/smack/RosterTest.java index 7afe40a43..9d1ec5d55 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/RosterTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/RosterTest.java @@ -19,7 +19,6 @@ package org.jivesoftware.smack; import static org.junit.Assert.*; -import java.io.StringReader; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CopyOnWriteArrayList; @@ -31,11 +30,11 @@ import org.jivesoftware.smack.packet.RosterPacket; import org.jivesoftware.smack.packet.IQ.Type; import org.jivesoftware.smack.packet.RosterPacket.Item; import org.jivesoftware.smack.packet.RosterPacket.ItemType; +import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smack.util.PacketParserUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlPullParser; /** @@ -315,8 +314,6 @@ public class RosterTest { final String contactJID = "nurse@example.com"; final Roster roster = connection.getRoster(); assertNotNull("Can't get the roster from the provided connection!", roster); - final XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); final StringBuilder sb = new StringBuilder(); sb.append("
*
* Warning: this is an non-standard protocol documented by
- * JEP-49. Because this is a
+ * XEP-49. Because this is a
* non-standard protocol, it is subject to change.
*
* @author Matt Tucker
@@ -285,7 +285,7 @@ public class PrivateDataManager extends Manager {
StringBuilder buf = new StringBuilder();
buf.append("
*
+ * XEP-54 documentation.
*
*
* The following link summarizes the requirements of XHTML IM:
- * Valid tags.
+ * Valid tags.
*
* Warning: this is an non-standard protocol documented by
- * JEP-71. Because this is a
+ * XEP-71. Because this is a
* non-standard protocol, it is subject to change.
*
* @author Gaston Dombiak
diff --git a/smack-extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers b/smack-extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers
index 9fcb25df6..c47651382 100644
--- a/smack-extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers
+++ b/smack-extensions/src/main/resources/org.jivesoftware.smackx/extensions.providers
@@ -172,7 +172,7 @@