1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2024-12-23 13:07:59 +01:00

Advanced Message Processing implementation (XEP-0079)

Fixes SMACK-544
This commit is contained in:
Vyacheslav Blinov 2014-03-07 17:46:46 +04:00 committed by Florian Schmaus
parent 06f88674ee
commit 61bf5cd4ce
10 changed files with 878 additions and 0 deletions

View file

@ -0,0 +1,83 @@
/**
*
* Copyright 2014 Vyacheslav Blinov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.amp;
import org.jivesoftware.smack.Connection;
public class AMPDeliverCondition implements AMPExtension.Condition {
public static final String NAME = "deliver";
/**
* Check if server supports deliver condition
* @param connection Smack connection instance
* @return true if deliver condition is supported.
*/
public static boolean isSupported(Connection connection) {
return AMPManager.isConditionSupported(connection, NAME);
}
private final Value value;
/**
* Create new amp deliver condition with value setted to one of defined by XEP-0079.
* See http://xmpp.org/extensions/xep-0079.html#conditions-def-deliver
* @param value AMPDeliveryCondition.Value instance that will be used as value parameter. Can't be null.
*/
public AMPDeliverCondition(Value value) {
if (value == null)
throw new NullPointerException("Can't create AMPDeliverCondition with null value");
this.value = value;
}
@Override
public String getName() {
return NAME;
}
@Override
public String getValue() {
return value.toString();
}
/**
* Value for amp deliver condition as defined by XEP-0079.
* See http://xmpp.org/extensions/xep-0079.html#conditions-def-deliver
*/
public static enum Value {
/**
* The message would be immediately delivered to the intended recipient or routed to the next hop.
*/
direct,
/**
* The message would be forwarded to another XMPP address or account.
*/
forward,
/**
* The message would be sent through a gateway to an address or account on a non-XMPP system.
*/
gateway,
/**
* The message would not be delivered at all (e.g., because the intended recipient is offline and message storage is not enabled).
*/
none,
/**
* The message would be stored offline for later delivery to the intended recipient.
*/
stored
}
}

View file

@ -0,0 +1,72 @@
/**
*
* Copyright 2014 Vyacheslav Blinov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.amp;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.util.XmppDateTime;
import java.util.Date;
public class AMPExpireAtCondition implements AMPExtension.Condition {
public static final String NAME = "expire-at";
/**
* Check if server supports expire-at condition
* @param connection Smack connection instance
* @return true if expire-at condition is supported.
*/
public static boolean isSupported(Connection connection) {
return AMPManager.isConditionSupported(connection, NAME);
}
private final String value;
/**
* Create new expire-at amp condition with value setted as XEP-0082 formatted date.
* @param utcDateTime Date instance of time
* that will be used as value parameter after formatting to XEP-0082 format. Can't be null.
*/
public AMPExpireAtCondition(Date utcDateTime) {
if (utcDateTime == null)
throw new NullPointerException("Can't create AMPExpireAtCondition with null value");
this.value = XmppDateTime.formatXEP0082Date(utcDateTime);
}
/**
* Create new expire-at amp condition with defined value.
* @param utcDateTime UTC time string that will be used as value parameter.
* Should be formatted as XEP-0082 Date format. Can't be null.
*/
public AMPExpireAtCondition(String utcDateTime) {
if (utcDateTime == null)
throw new NullPointerException("Can't create AMPExpireAtCondition with null value");
this.value = utcDateTime;
}
@Override
public String getName() {
return NAME;
}
@Override
public String getValue() {
return value;
}
}

View file

@ -0,0 +1,273 @@
/**
*
* Copyright 2014 Vyacheslav Blinov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.amp;
import org.jivesoftware.smack.packet.PacketExtension;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class AMPExtension implements PacketExtension {
public static final String NAMESPACE = "http://jabber.org/protocol/amp";
public static final String ELEMENT = "amp";
private CopyOnWriteArrayList<Rule> rules = new CopyOnWriteArrayList<Rule>();
private boolean perHop = false;
private final String from;
private final String to;
private final Status status;
/**
* Create a new AMPExtension instance with defined from, to and status attributes. Used to create incoming packets.
* @param from jid that triggered this amp callback.
* @param to receiver of this amp receipt.
* @param status status of this amp receipt.
*/
public AMPExtension(String from, String to, Status status) {
this.from = from;
this.to = to;
this.status = status;
}
/**
* Create a new amp request extension to be used with outgoing message.
*/
public AMPExtension() {
this.from = null;
this.to = null;
this.status = null;
}
/**
* @return jid that triggered this amp callback.
*/
public String getFrom() {
return from;
}
/**
* @return receiver of this amp receipt.
*/
public String getTo() {
return to;
}
/**
* Status of this amp notification
* @return Status for this amp
*/
public Status getStatus() {
return status;
}
/**
* Returns an Iterator for the rules in the packet.
*
* @return an Iterator for the rules in the packet.
*/
public Iterator<Rule> getRules() {
return Collections.unmodifiableList(new ArrayList<Rule>(rules)).iterator();
}
/**
* Adds a rule to the amp element. Amp can have any number of rules.
*
* @param rule the rule to add.
*/
public void addRule(Rule rule) {
rules.add(rule);
}
/**
* Returns a count of the rules in the AMP packet.
*
* @return the number of rules in the AMP packet.
*/
public int getRulesCount() {
return rules.size();
}
/**
* Sets this amp ruleset to be "per-hop".
*
* @param enabled true if "per-hop" should be enabled
*/
public synchronized void setPerHop(boolean enabled) {
perHop = enabled;
}
/**
* Returns true is this ruleset is "per-hop".
*
* @return true is this ruleset is "per-hop".
*/
public synchronized boolean isPerHop() {
return perHop;
}
/**
* Returns the XML element name of the extension sub-packet root element.
* Always returns "amp"
*
* @return the XML element name of the packet extension.
*/
@Override
public String getElementName() {
return ELEMENT;
}
/**
* Returns the XML namespace of the extension sub-packet root element.
* According the specification the namespace is always "http://jabber.org/protocol/xhtml-im"
*
* @return the XML namespace of the packet extension.
*/
@Override
public String getNamespace() {
return NAMESPACE;
}
/**
* Returns the XML representation of a XHTML extension according the specification.
**/
@Override
public String toXML() {
StringBuilder buf = new StringBuilder();
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\"");
if (status != null) {
buf.append(" status=\"").append(status.toString()).append("\"");
}
if (to != null) {
buf.append(" to=\"").append(to).append("\"");
}
if (from != null) {
buf.append(" from=\"").append(from).append("\"");
}
if (perHop) {
buf.append(" per-hop=\"true\"");
}
buf.append(">");
// Loop through all the rules and append them to the string buffer
for (Iterator<Rule> i = getRules(); i.hasNext();) {
buf.append(i.next().toXML());
}
buf.append("</").append(getElementName()).append(">");
return buf.toString();
}
/**
* XEP-0079 Rule element. Defines AMP Rule parameters. Can be added to AMPExtension.
*/
public static class Rule {
public static final String ELEMENT = "rule";
private final Action action;
private final Condition condition;
public Action getAction() {
return action;
}
public Condition getCondition() {
return condition;
}
/**
* Create a new amp rule with specified action and condition. Value will be taken from condition argument
* @param action action for this rule
* @param condition condition for this rule
*/
public Rule(Action action, Condition condition) {
if (action == null)
throw new NullPointerException("Can't create Rule with null action");
if (condition == null)
throw new NullPointerException("Can't create Rule with null condition");
this.action = action;
this.condition = condition;
}
private String toXML() {
return "<" + ELEMENT + " " + Action.ATTRIBUTE_NAME + "=\"" + action.toString() + "\" " +
Condition.ATTRIBUTE_NAME + "=\"" + condition.getName() + "\" " +
"value=\"" + condition.getValue() + "\"/>";
}
}
/**
* Interface for defining XEP-0079 Conditions and their values
* @see AMPDeliverCondition
* @see AMPExpireAtCondition
* @see AMPMatchResourceCondition
**/
public static interface Condition {
String getName();
String getValue();
static final String ATTRIBUTE_NAME="condition";
}
/**
* amp action attribute
* See http://xmpp.org/extensions/xep-0079.html#actions-def
**/
public static enum Action {
/**
* The "alert" action triggers a reply <message/> stanza to the sending entity.
* This <message/> stanza MUST contain the element <amp status='alert'/>,
* which itself contains the <rule/> that triggered this action. In all other respects,
* this action behaves as "drop".
*/
alert,
/**
* The "drop" action silently discards the message from any further delivery attempts
* and ensures that it is not placed into offline storage.
* The drop MUST NOT result in other responses.
*/
drop,
/**
* The "error" action triggers a reply <message/> stanza of type "error" to the sending entity.
* The <message/> stanza's <error/> child MUST contain a
* <failed-rules xmlns='http://jabber.org/protocol/amp#errors'/> error condition,
* which itself contains the rules that triggered this action.
*/
error,
/**
* The "notify" action triggers a reply <message/> stanza to the sending entity.
* This <message/> stanza MUST contain the element <amp status='notify'/>, which itself
* contains the <rule/> that triggered this action. Unlike the other actions,
* this action does not override the default behavior for a server.
* Instead, the server then executes its default behavior after sending the notify.
*/
notify;
static final String ATTRIBUTE_NAME="action";
}
/**
* amp notification status as defined by XEP-0079
*/
public static enum Status {
alert,
error,
notify
}
}

View file

@ -0,0 +1,125 @@
/**
*
* Copyright 2014 Vyacheslav Blinov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.amp;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.xmlpull.v1.XmlPullParser;
public class AMPExtensionProvider implements PacketExtensionProvider {
/**
* Creates a new AMPExtensionProvider.
* ProviderManager requires that every PacketExtensionProvider has a public, no-argument constructor
*/
public AMPExtensionProvider() {}
/**
* Parses a AMPExtension packet (extension sub-packet).
*
* @param parser the XML parser, positioned at the starting element of the extension.
* @return a PacketExtension.
* @throws Exception if a parsing error occurs.
*/
@Override
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
final String from = parser.getAttributeValue(null, "from");
final String to = parser.getAttributeValue(null, "to");
final String statusString = parser.getAttributeValue(null, "status");
AMPExtension.Status status = null;
if (statusString != null) {
try {
status = AMPExtension.Status.valueOf(statusString);
} catch (IllegalArgumentException ex) {
System.err.println("Found invalid amp status " + statusString);
}
}
AMPExtension ampExtension = new AMPExtension(from, to, status);
String perHopValue = parser.getAttributeValue(null, "per-hop");
if (perHopValue != null) {
boolean perHop = Boolean.parseBoolean(perHopValue);
ampExtension.setPerHop(perHop);
}
boolean done = false;
while (!done) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals(AMPExtension.Rule.ELEMENT)) {
String actionString = parser.getAttributeValue(null, AMPExtension.Action.ATTRIBUTE_NAME);
String conditionName = parser.getAttributeValue(null, AMPExtension.Condition.ATTRIBUTE_NAME);
String conditionValue = parser.getAttributeValue(null, "value");
AMPExtension.Condition condition = createCondition(conditionName, conditionValue);
AMPExtension.Action action = null;
if (actionString != null) {
try {
action = AMPExtension.Action.valueOf(actionString);
} catch (IllegalArgumentException ex) {
System.err.println("Found invalid rule action value " + actionString);
}
}
if (action == null || condition == null) {
System.err.println("Rule is skipped because either it's action or it's condition is invalid");
} else {
AMPExtension.Rule rule = new AMPExtension.Rule(action, condition);
ampExtension.addRule(rule);
}
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals(AMPExtension.ELEMENT)) {
done = true;
}
}
}
return ampExtension;
}
private AMPExtension.Condition createCondition(String name, String value) {
if (name == null || value == null) {
System.err.println("Can't create rule condition from null name and/or value");
return null;
}
if (AMPDeliverCondition.NAME.equals(name)) {
try {
return new AMPDeliverCondition(AMPDeliverCondition.Value.valueOf(value));
} catch (IllegalArgumentException ex) {
System.err.println("Found invalid rule delivery condition value " + value);
return null;
}
} else if (AMPExpireAtCondition.NAME.equals(name)) {
return new AMPExpireAtCondition(value);
} else if (AMPMatchResourceCondition.NAME.equals(name)) {
try {
return new AMPMatchResourceCondition(AMPMatchResourceCondition.Value.valueOf(value));
} catch (IllegalArgumentException ex) {
System.err.println("Found invalid rule match-resource condition value " + value);
return null;
}
} else {
System.err.println("Found unknown rule condition name " + name);
return null;
}
}
}

View file

@ -0,0 +1,121 @@
/**
*
* Copyright 2014 Vyacheslav Blinov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.amp;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
import java.util.Iterator;
/**
* Manages AMP stanzas within messages. A AMPManager provides a high level access to
* get and set AMP rules to messages.
*
* See http://xmpp.org/extensions/xep-0079.html for AMP extension details
*
* @author Vyacheslav Blinov
*/
public class AMPManager {
// Enable the AMP support on every established connection
// The ServiceDiscoveryManager class should have been already initialized
static {
Connection.addConnectionCreationListener(new ConnectionCreationListener() {
public void connectionCreated(Connection connection) {
AMPManager.setServiceEnabled(connection, true);
}
});
}
/**
* Enables or disables the AMP support on a given connection.<p>
*
* Before starting to send AMP messages to a user, check that the user can handle XHTML
* messages. Enable the AMP support to indicate that this client handles XHTML messages.
*
* @param connection the connection where the service will be enabled or disabled
* @param enabled indicates if the service will be enabled or disabled
*/
public synchronized static void setServiceEnabled(Connection connection, boolean enabled) {
if (isServiceEnabled(connection) == enabled)
return;
if (enabled) {
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(AMPExtension.NAMESPACE);
}
else {
ServiceDiscoveryManager.getInstanceFor(connection).removeFeature(AMPExtension.NAMESPACE);
}
}
/**
* Returns true if the AMP support is enabled for the given connection.
*
* @param connection the connection to look for AMP support
* @return a boolean indicating if the AMP support is enabled for the given connection
*/
public static boolean isServiceEnabled(Connection connection) {
connection.getServiceName();
return ServiceDiscoveryManager.getInstanceFor(connection).includesFeature(AMPExtension.NAMESPACE);
}
/**
* Check if server supports specified action
* @param connection active xmpp connection
* @param action action to check
* @return true if this action is supported.
*/
public static boolean isActionSupported(Connection connection, AMPExtension.Action action) {
String featureName = AMPExtension.NAMESPACE + "?action=" + action.toString();
return isFeatureSupportedByServer(connection, featureName, AMPExtension.NAMESPACE);
}
/**
* Check if server supports specified condition
* @param connection active xmpp connection
* @param conditionName name of condition to check
* @return true if this condition is supported.
* @see AMPDeliverCondition
* @see AMPExpireAtCondition
* @see AMPMatchResourceCondition
*/
public static boolean isConditionSupported(Connection connection, String conditionName) {
String featureName = AMPExtension.NAMESPACE + "?condition=" + conditionName;
return isFeatureSupportedByServer(connection, featureName, AMPExtension.NAMESPACE);
}
private static boolean isFeatureSupportedByServer(Connection connection, String featureName, String node) {
try {
ServiceDiscoveryManager discoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
DiscoverInfo info = discoveryManager.discoverInfo(connection.getServiceName(), node);
Iterator<DiscoverInfo.Feature> it = info.getFeatures();
while (it.hasNext()) {
DiscoverInfo.Feature feature = it.next();
if (featureName.equals(feature.getVar())) {
return true;
}
}
} catch (XMPPException e) {
e.printStackTrace();
}
return false;
}
}

View file

@ -0,0 +1,78 @@
/**
*
* Copyright 2014 Vyacheslav Blinov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.amp;
import org.jivesoftware.smack.Connection;
public class AMPMatchResourceCondition implements AMPExtension.Condition {
public static final String NAME = "match-resource";
/**
* Check if server supports match-resource condition
* @param connection Smack connection instance
* @return true if match-resource condition is supported.
*/
public static boolean isSupported(Connection connection) {
return AMPManager.isConditionSupported(connection, NAME);
}
private final Value value;
/**
* Create new amp match-resource condition with value setted to one of defined by XEP-0079.
* See http://xmpp.org/extensions/xep-0079.html#conditions-def-match
* @param value AMPDeliveryCondition.Value instance that will be used as value parameter. Can't be null.
*/
public AMPMatchResourceCondition(Value value) {
if (value == null)
throw new NullPointerException("Can't create AMPMatchResourceCondition with null value");
this.value = value;
}
@Override
public String getName() {
return NAME;
}
@Override
public String getValue() {
return value.toString();
}
/**
* match-resource amp condition value as defined by XEP-0079
* See http://xmpp.org/extensions/xep-0079.html#conditions-def-match
*/
public static enum Value {
/**
* Destination resource matches any value, effectively ignoring the intended resource.
* Example: "home/laptop" matches "home", "home/desktop" or "work/desktop"
*/
any,
/**
* Destination resource exactly matches the intended resource.
* Example: "home/laptop" only matches "home/laptop" and not "home/desktop" or "work/desktop"
*/
exact,
/**
* Destination resource matches any value except for the intended resource.
* Example: "home/laptop" matches "work/desktop", "home" or "home/desktop", but not "home/laptop"
*/
other
}
}

View file

@ -449,4 +449,11 @@
<className>org.jivesoftware.smackx.privacy.provider.PrivacyProvider</className> <className>org.jivesoftware.smackx.privacy.provider.PrivacyProvider</className>
</iqProvider> </iqProvider>
<!-- XEP-0079 Advanced Message Processing -->
<extensionProvider>
<elementName>amp</elementName>
<namespace>http://jabber.org/protocol/amp</namespace>
<className>org.jivesoftware.smackx.amp.AMPExtensionProvider</className>
</extensionProvider>
</smackProviders> </smackProviders>

View file

@ -0,0 +1,105 @@
/**
*
* Copyright 2014 Vyacheslav Blinov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.amp;
import org.jivesoftware.smack.packet.PacketExtension;
import org.junit.Before;
import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class AMPExtensionTest {
private InputStream CORRECT_SENDING_STANZA_STREAM;
private InputStream INCORRECT_RECEIVING_STANZA_STREAM;
@Before
public void setUp() throws IOException {
CORRECT_SENDING_STANZA_STREAM = getClass().getResourceAsStream("correct_stanza_test.xml");
INCORRECT_RECEIVING_STANZA_STREAM = getClass().getResourceAsStream("incorrect_stanza_test.xml");
}
@Test
public void isCorrectToXmlTransform() throws IOException {
String correctStanza = toString(CORRECT_SENDING_STANZA_STREAM);
AMPExtension ext = new AMPExtension();
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.alert, new AMPDeliverCondition(AMPDeliverCondition.Value.direct)));
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.drop, new AMPDeliverCondition(AMPDeliverCondition.Value.forward)));
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.error, new AMPDeliverCondition(AMPDeliverCondition.Value.gateway)));
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.notify, new AMPDeliverCondition(AMPDeliverCondition.Value.none)));
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.notify, new AMPDeliverCondition(AMPDeliverCondition.Value.stored)));
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.notify, new AMPExpireAtCondition("2004-09-10T08:33:14Z")));
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.notify, new AMPMatchResourceCondition(AMPMatchResourceCondition.Value.any)));
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.notify, new AMPMatchResourceCondition(AMPMatchResourceCondition.Value.exact)));
ext.addRule(new AMPExtension.Rule(AMPExtension.Action.notify, new AMPMatchResourceCondition(AMPMatchResourceCondition.Value.other)));
assertEquals(correctStanza, ext.toXML());
}
@Test
public void isCorrectFromXmlErrorHandling() throws Exception {
AMPExtensionProvider ampProvider = new AMPExtensionProvider();
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(INCORRECT_RECEIVING_STANZA_STREAM, "UTF-8");
assertEquals(XmlPullParser.START_TAG, parser.next());
assertEquals(AMPExtension.ELEMENT, parser.getName());
PacketExtension extension = ampProvider.parseExtension(parser);
assertTrue(extension instanceof AMPExtension);
AMPExtension amp = (AMPExtension) extension;
assertEquals(0, amp.getRulesCount());
assertEquals(AMPExtension.Status.alert, amp.getStatus());
assertEquals("bernardo@hamlet.lit/elsinore", amp.getFrom());
assertEquals("francisco@hamlet.lit", amp.getTo());
}
@Test
public void isCorrectFromXmlDeserialization() throws Exception {
AMPExtensionProvider ampProvider = new AMPExtensionProvider();
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(CORRECT_SENDING_STANZA_STREAM, "UTF-8");
assertEquals(XmlPullParser.START_TAG, parser.next());
assertEquals(AMPExtension.ELEMENT, parser.getName());
PacketExtension extension = ampProvider.parseExtension(parser);
assertTrue(extension instanceof AMPExtension);
AMPExtension amp = (AMPExtension) extension;
assertEquals(9, amp.getRulesCount());
}
private String toString(InputStream stream) throws IOException {
byte[] data = new byte[stream.available()];
stream.read(data);
stream.close();
return new String(data, Charset.defaultCharset());
}
}

View file

@ -0,0 +1 @@
<amp xmlns="http://jabber.org/protocol/amp"><rule action="alert" condition="deliver" value="direct"/><rule action="drop" condition="deliver" value="forward"/><rule action="error" condition="deliver" value="gateway"/><rule action="notify" condition="deliver" value="none"/><rule action="notify" condition="deliver" value="stored"/><rule action="notify" condition="expire-at" value="2004-09-10T08:33:14Z"/><rule action="notify" condition="match-resource" value="any"/><rule action="notify" condition="match-resource" value="exact"/><rule action="notify" condition="match-resource" value="other"/></amp>

View file

@ -0,0 +1,13 @@
<amp xmlns='http://jabber.org/protocol/amp'
status='alert'
from='bernardo@hamlet.lit/elsinore'
to='francisco@hamlet.lit'>
<rule action='test' condition='deliver' value='direct'/>
<rule action='drop' condition='deliver' value='forwarded'/>
<rule action='error' condition='delivered' value='gateway'/>
<rule action='notify' value='none'/>
<rule action='notify' condition='deliver'/>
<rule condition='expire-at' value='2004-09-10T08:33:14Z'/>
<rule condition='expire-at' />
<rule action='notify' condition='match-resource' value='anys'/>
</amp>