1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-09-27 18:19:33 +02:00
Smack/smack-core/src/main/java/org/jivesoftware/smack/packet/Packet.java
Florian Schmaus 9286a1decb Rework XMPP Error class design
Introduce AbstractError, change 'Conditions' to enums. Because of
AbstractError, it was necessary that PlainStreamElement and
TopLevelStreamElement becomes an interface. Thus the implementation of
TopLevelStreamElement.toString() had to be removed.

This adds

- policy-violation
- unexpected-request

to XMPPError.Condition, and removes the

- payment-required
- remote-server-error
- unexpected-condition
- request-timeout

Conditions

The file transfer code does now no longer throw XMPPErrorExceptions, but
SmackExceptions.

Fixes SMACK-608. Makes it possible to resolves SMACK-386.
2014-11-25 13:19:32 +01:00

399 lines
13 KiB
Java

/**
*
* Copyright 2003-2007 Jive Software.
*
* 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.smack.packet;
import org.jivesoftware.smack.util.PacketUtil;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jxmpp.util.XmppStringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
/**
* Base class for XMPP Stanzas, which are called packets in Smack.
* <p>
* Every packet has a unique ID (which is automatically
* generated, but can be overridden). Optionally, the "to" and "from" fields can be set.
* </p>
* <p>
* XMPP Stanzas are {@link Message}, {@link IQ} and {@link Presence}. Which therefore subclass this class.
* </p>
*
* @author Matt Tucker
*/
public abstract class Packet implements TopLevelStreamElement {
public static final String TEXT = "text";
public static final String ITEM = "item";
protected static final String DEFAULT_LANGUAGE =
java.util.Locale.getDefault().getLanguage().toLowerCase(Locale.US);
/**
* A prefix helps to make sure that ID's are unique across multiple instances.
*/
private static final String prefix = StringUtils.randomString(5) + "-";
/**
* Keeps track of the current increment, which is appended to the prefix to
* forum a unique ID.
*/
private static final AtomicLong id = new AtomicLong();
private final Map<String, PacketExtension> packetExtensions = new LinkedHashMap<String, PacketExtension>(12);
private String packetID = null;
private String to = null;
private String from = null;
private XMPPError error = null;
/**
* Optional value of the 'xml:lang' attribute of the outermost element of
* the stanza.
* <p>
* Such an attribute is defined for all stanza types. For IQ, see for
* example XEP-50 3.7:
* "The requester SHOULD provide its locale information using the "xml:lang
* " attribute on either the <iq/> (RECOMMENDED) or <command/> element."
* </p>
*/
protected String language;
public Packet() {
this(prefix + Long.toString(id.incrementAndGet()));
}
public Packet(String packetID) {
setPacketID(packetID);
}
public Packet(Packet p) {
packetID = p.getPacketID();
to = p.getTo();
from = p.getFrom();
error = p.error;
// Copy extensions
for (PacketExtension pe : p.getExtensions()) {
addExtension(pe);
}
}
/**
* Returns the unique ID of the packet. The returned value could be <tt>null</tt> when
* ID_NOT_AVAILABLE was set as the packet's id.
*
* @return the packet's unique ID or <tt>null</tt> if the packet's id is not available.
*/
public String getPacketID() {
return packetID;
}
/**
* Sets the unique ID of the packet. To indicate that a packet has no id
* pass the constant ID_NOT_AVAILABLE as the packet's id value.
*
* @param packetID the unique ID for the packet.
*/
public void setPacketID(String packetID) {
this.packetID = packetID;
}
/**
* Returns who the packet is being sent "to", or <tt>null</tt> if
* the value is not set. The XMPP protocol often makes the "to"
* attribute optional, so it does not always need to be set.<p>
*
* @return who the packet is being sent to, or <tt>null</tt> if the
* value has not been set.
*/
public String getTo() {
return to;
}
/**
* Sets who the packet is being sent "to". The XMPP protocol often makes
* the "to" attribute optional, so it does not always need to be set.
*
* @param to who the packet is being sent to.
*/
public void setTo(String to) {
this.to = to;
}
/**
* Returns who the packet is being sent "from" or <tt>null</tt> if
* the value is not set. The XMPP protocol often makes the "from"
* attribute optional, so it does not always need to be set.<p>
*
* @return who the packet is being sent from, or <tt>null</tt> if the
* value has not been set.
*/
public String getFrom() {
return from;
}
/**
* Sets who the packet is being sent "from". The XMPP protocol often
* makes the "from" attribute optional, so it does not always need to
* be set.
*
* @param from who the packet is being sent to.
*/
public void setFrom(String from) {
this.from = from;
}
/**
* Returns the error associated with this packet, or <tt>null</tt> if there are
* no errors.
*
* @return the error sub-packet or <tt>null</tt> if there isn't an error.
*/
public XMPPError getError() {
return error;
}
/**
* Sets the error for this packet.
*
* @param error the error to associate with this packet.
*/
public void setError(XMPPError error) {
this.error = error;
}
/**
* Returns the xml:lang of this Stanza, or null if one has not been set.
*
* @return the xml:lang of this Stanza, or null.
*/
public String getLanguage() {
return language;
}
/**
* Sets the xml:lang of this Stanza.
*
* @param language the xml:lang of this Stanza.
*/
public void setLanguage(String language) {
this.language = language;
}
/**
* Returns a copy of the packet extensions attached to the packet.
*
* @return the packet extensions.
*/
public List<PacketExtension> getExtensions() {
synchronized (packetExtensions) {
if (packetExtensions.isEmpty()) {
return Collections.emptyList();
}
return new ArrayList<PacketExtension>(packetExtensions.values());
}
}
/**
* Returns the first extension of this packet that has the given namespace.
* <p>
* When possible, use {@link #getExtension(String,String)} instead.
* </p>
*
* @param namespace the namespace of the extension that is desired.
* @return the packet extension with the given namespace.
*/
public PacketExtension getExtension(String namespace) {
return PacketUtil.packetExtensionfromCollection(getExtensions(), null, namespace);
}
/**
* Returns the first packet extension that matches the specified element name and
* namespace, or <tt>null</tt> if it doesn't exist. If the provided elementName is null,
* only the namespace is matched. Packet extensions are
* are arbitrary XML sub-documents in standard XMPP packets. By default, a
* DefaultPacketExtension instance will be returned for each extension. However,
* PacketExtensionProvider instances can be registered with the
* {@link org.jivesoftware.smack.provider.ProviderManager ProviderManager}
* class to handle custom parsing. In that case, the type of the Object
* will be determined by the provider.
*
* @param elementName the XML element name of the packet extension. (May be null)
* @param namespace the XML element namespace of the packet extension.
* @return the extension, or <tt>null</tt> if it doesn't exist.
*/
@SuppressWarnings("unchecked")
public <PE extends PacketExtension> PE getExtension(String elementName, String namespace) {
if (namespace == null) {
return null;
}
String key = XmppStringUtils.generateKey(elementName, namespace);
PacketExtension packetExtension;
synchronized (packetExtensions) {
packetExtension = packetExtensions.get(key);
}
if (packetExtension == null) {
return null;
}
return (PE) packetExtension;
}
/**
* Adds a packet extension to the packet. Does nothing if extension is null.
*
* @param extension a packet extension.
*/
public void addExtension(PacketExtension extension) {
if (extension == null) return;
String key = XmppStringUtils.generateKey(extension.getElementName(), extension.getNamespace());
synchronized (packetExtensions) {
packetExtensions.put(key, extension);
}
}
/**
* Adds a collection of packet extensions to the packet. Does nothing if extensions is null.
*
* @param extensions a collection of packet extensions
*/
public void addExtensions(Collection<PacketExtension> extensions) {
if (extensions == null) return;
for (PacketExtension packetExtension : extensions) {
addExtension(packetExtension);
}
}
/**
* Check if a packet extension with the given element and namespace exists.
* <p>
* The argument <code>elementName</code> may be null.
* </p>
*
* @param elementName
* @param namespace
* @return true if a packet extension exists, false otherwise.
*/
public boolean hasExtension(String elementName, String namespace) {
if (elementName == null) {
return hasExtension(namespace);
}
String key = XmppStringUtils.generateKey(elementName, namespace);
synchronized (packetExtensions) {
return packetExtensions.containsKey(key);
}
}
/**
* Check if a packet extension with the given namespace exists.
*
* @param namespace
* @return true if a packet extension exists, false otherwise.
*/
public boolean hasExtension(String namespace) {
synchronized (packetExtensions) {
for (PacketExtension packetExtension : packetExtensions.values()) {
if (packetExtension.getNamespace().equals(namespace)) {
return true;
}
}
}
return false;
}
/**
* Remove the packet extension with the given elementName and namespace.
*
* @param elementName
* @param namespace
* @return the removed packet extension or null.
*/
public PacketExtension removeExtension(String elementName, String namespace) {
String key = XmppStringUtils.generateKey(elementName, namespace);
synchronized (packetExtensions) {
return packetExtensions.remove(key);
}
}
/**
* Removes a packet extension from the packet.
*
* @param extension the packet extension to remove.
* @return the removed packet extension or null.
*/
public PacketExtension removeExtension(PacketExtension extension) {
return removeExtension(extension.getElementName(), extension.getNamespace());
}
/**
* Returns the extension sub-packets (including properties data) as an XML
* String, or the Empty String if there are no packet extensions.
*
* @return the extension sub-packets as XML or the Empty String if there
* are no packet extensions.
*/
protected final XmlStringBuilder getExtensionsXML() {
XmlStringBuilder xml = new XmlStringBuilder();
// Add in all standard extension sub-packets.
for (PacketExtension extension : getExtensions()) {
xml.append(extension.toXML());
}
return xml;
}
/**
* Returns the default language used for all messages containing localized content.
*
* @return the default language
*/
public static String getDefaultLanguage() {
return DEFAULT_LANGUAGE;
}
/**
* Add to, from, id and 'xml:lang' attributes
*
* @param xml
*/
protected void addCommonAttributes(XmlStringBuilder xml) {
xml.optAttribute("to", getTo());
xml.optAttribute("from", getFrom());
xml.optAttribute("id", getPacketID());
xml.xmllangAttribute(getLanguage());
}
/**
* Append an XMPPError is this packet has one set.
*
* @param xml the XmlStringBuilder to append the error to.
*/
protected void appendErrorIfExists(XmlStringBuilder xml) {
XMPPError error = getError();
if (error != null) {
xml.append(error.toXML());
}
}
}