Add (partial) support for IoT XEPs

That is XEP-0323, -0324, -0325, and -0347.

SMACK-727.
This commit is contained in:
Florian Schmaus 2016-07-20 20:57:04 +02:00
parent d1fe5c2933
commit b91978dcc4
110 changed files with 5395 additions and 40 deletions

View File

@ -55,6 +55,8 @@ debug=true
| accountOnePassword | Password of the first XMPP account | | accountOnePassword | Password of the first XMPP account |
| accountTwoUsername | Username of the second XMPP account | | accountTwoUsername | Username of the second XMPP account |
| accountTwoPassword | Password of the second XMPP account | | accountTwoPassword | Password of the second XMPP account |
| accountThreeUsername | Username of the third XMPP account |
| accountThreePassword | Password of the third XMPP account |
| debug | 'true' to enable debug output | | debug | 'true' to enable debug output |
| enabledTests | List of enabled tests | | enabledTests | List of enabled tests |
| disabledTests | List of disabled tests | | disabledTests | List of disabled tests |

View File

@ -1,4 +1,4 @@
Smack Extensions User Manual smackSmack Extensions User Manual
============================ ============================
The XMPP protocol includes a base protocol and many optional extensions The XMPP protocol includes a base protocol and many optional extensions
@ -77,8 +77,12 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental
| Name | XEP | Description | | Name | XEP | Description |
|---------------------------------------------|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------| |---------------------------------------------|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------|
| Message Carbons | [XEP-0280](http://xmpp.org/extensions/xep-0280.html) | Keep all IM clients for a user engaged in a conversation, by carbon-copy outbound messages to all interested resources. | Message Carbons | [XEP-0280](http://xmpp.org/extensions/xep-0280.html) | Keep all IM clients for a user engaged in a conversation, by carbon-copy outbound messages to all interested resources.
| [HTTP over XMPP transport](hoxt.md) | [XEP-0332](http://xmpp.org/extensions/xep-0332.html) | Allows to transport HTTP communication over XMPP peer-to-peer networks. | | [Internet of Things - Sensor Data](iot.md) | [XEP-0323](http://xmpp.org/extensions/xep-0323.html) | Sensor data interchange over XMPP. |
| [Internet of Things - Provisioning](iot.md) | [XEP-0324](http://xmpp.org/extensions/xep-0324.html) | Provisioning, access rights and user priviliges for the Internet of Things. |
| [Internet of Things - Control](iot.md) | [XEP-0325](http://xmpp.org/extensions/xep-0325.html) | Describes how to control devices or actuators in an XMPP-based sensor netowrk. |
| [HTTP over XMPP transport](hoxt.md) | [XEP-0332](http://xmpp.org/extensions/xep-0332.html) | Allows to transport HTTP communication over XMPP peer-to-peer networks. |
| JSON Containers | [XEP-0335](http://xmpp.org/extensions/xep-0335.html) | Encapsulation of JSON data within XMPP Stanzas. | | JSON Containers | [XEP-0335](http://xmpp.org/extensions/xep-0335.html) | Encapsulation of JSON data within XMPP Stanzas. |
| [Internet of Things - Discovery](iot.md) | [XEP-0347](http://xmpp.org/extensions/xep-0347.html) | Describes how Things can be installed and discovered by their owners. |
| Google GCM JSON payload | n/a | Semantically the same as XEP-0335: JSON Containers | | Google GCM JSON payload | n/a | Semantically the same as XEP-0335: JSON Containers |
| Client State Indication | [XEP-0352](http://xmpp.org/extensions/xep-0352.html) | A way for the client to indicate its active/inactive state. | | Client State Indication | [XEP-0352](http://xmpp.org/extensions/xep-0352.html) | A way for the client to indicate its active/inactive state. |

View File

@ -0,0 +1,116 @@
Internet of Things (XEP-0323, -0324, -0325, -0347)
==================================================
The Internet of Things (IoT) XEPs are an experimental open standard how XMPP can be used for IoT. They currently consists of
- XEP-0323 Sensor Data
- XEP-0324 Provisioning
- XEP-0325 Control
- XEP-0326 Concentrators
- XEP-0347 Discovery
Smack only supports a subset of the functionality described by the XEPs!
Thing Builder
-------------
The `org.jivesoftware.smackx.iot.Thing` class acts as basic entity representing a single "Thing" which can used to retrieve data from or to send control commands to. `Things` are constructed using a builder API.
Reading data from things
------------------------
For example, we can build a Thing which provides the current temperature with
```java
Thing dataThing = Thing.builder().setKey(key).setSerialNumber(sn).setMomentaryReadOutRequestHandler(new ThingMomentaryReadOutRequest() {
@Override
public void momentaryReadOutRequest(ThingMomentaryReadOutResult callback) {
int temp = getCurrentTemperature();
IoTDataField.IntField field = new IntField("temperature", temp);
callback.momentaryReadOut(Collections.singletonList(field));
}
}).build();
```
While not strictly required, most things are identified via a key and serial number. We also build the thing with a "momentary read out request handler" which when triggered, retrieved the current temperature and reports it back to the requestor.
After the `Thing` is build, it needs to be made available so that other entities within the federated XMPP network can use it. Right now, we only intall the Thing in the `IoTDataManager`, which means the thing will act on read out requests but not be managed by a provisioning server.
```java
IoTDataManager iotDataManager = IoTDataManager.getInstanceFor(connection);
iotDataManager.installThing(thing);
```
The data can be read out also by using the `IoTDataManager`:
```java
FullJid jid = …
List<IoTFieldsExtension> values = iotDataManager.requestMomentaryValuesReadOut(jid);
```
Now you have to unwrap the `IoTDataField` instances from the `IoTFieldsExtension`. Note that Smack currently only supports a subset of the specified data types.
Controlling a thing
-------------------
Things can also be controlled, e.g. to turn on a light. Let's create thing which can be used to turn the light on and off.
```java
Thing controlThing = Thing.builder().setKey(key).setSerialNumber(sn).setControlRequestHandler(new ThingControlRequest() {
@Override
public void processRequest(Jid from, Collection<SetData> setData) throws XMPPErrorException {
for (final SetData data : setData) {
if (!data.getName().equals("light")) continue;
if (!(data instanceof SetBoolData)) continue;
SetBoolData boolData = (SetBoolData) data;
setLight(boolData.getBooleanValue());
}
}
}).build();
```
No we have to install this thing into the `IoTControlManager`:
```java
IoTControlManager iotControlManager = IoTControlManager.getInstanceFor(connection);
iotControlManager.installThing(thing);
```
The `IoTControlManager` can also be used to control a thing:
```java
FullJid jid = …
SetData setData = new SetBoolData("light", true);
iotControlManager.setUsingIq(jid, setData);
```
Smack currently only supports a subset of the possible data types for set data.
Discovery
---------
You may wondered how a full JIDs of things can be determined. One approach is using the discovery mechanisms specified in XEP-0347. Smack provides the `IoTDiscoveryManager` as API for this.
For example, instead of just installing the previous things in the `IoTDataManager` and/or `IoTControlManager`, we could also use the `IoTDiscoveryManger` to register the thing with a registry. Doing thing also installs the thing in the `IoTDataManager` and the `IoTControlManager`.
```java
IoTDiscoveryManager iotDiscoveryManager = IoTDiscoveryManager.getInstanceFor(connection);
iotDiscovyerManager.registerThing(thing);
```
The registry will now make the thing known to a broader audience, and available for a potential owner.
The `IoTDiscoveryManager` can also be used to claim, disown, remove and unregister a thing.
Provisioning
------------
Things can usually only be used by other things if they are friends. Since a thing normally can't decide on its own if an incoming friendship request should be granted or not, we can delegate this decission to a provisioning service. Smack provides the `IoTProvisinoManager` to deal with friendship and provisioning.
For example, if you want to befriend another thing:
```java
BareJid jid = …
IoTProvisioningManager iotProvisioningManager = IoTProvisioningManager.getInstanceFor(connection);
iotProvisioningManager.sendFriendshipRequest(jid);
```

View File

@ -238,7 +238,11 @@ public final class ReconnectionManager {
// Makes a reconnection attempt // Makes a reconnection attempt
try { try {
if (isReconnectionPossible(connection)) { if (isReconnectionPossible(connection)) {
connection.connect(); try {
connection.connect();
} catch (SmackException.AlreadyConnectedException e) {
LOGGER.log(Level.FINER, "Connection was already connected on reconnection attempt", e);
}
} }
// TODO Starting with Smack 4.2, connect() will no // TODO Starting with Smack 4.2, connect() will no
// longer login automatically. So change this and the // longer login automatically. So change this and the

View File

@ -25,13 +25,7 @@ public class EmptyResultIQ extends IQ {
public EmptyResultIQ(IQ request) { public EmptyResultIQ(IQ request) {
this(); this();
if (!(request.getType() == Type.get || request.getType() == Type.set)) { initialzeAsResultFor(request);
throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
}
setStanzaId(request.getStanzaId());
setFrom(request.getTo());
setTo(request.getFrom());
} }
@Override @Override

View File

@ -215,6 +215,17 @@ public abstract class IQ extends Stanza {
*/ */
protected abstract IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml); protected abstract IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml);
protected final void initialzeAsResultFor(IQ request) {
if (!(request.getType() == Type.get || request.getType() == Type.set)) {
throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
}
setStanzaId(request.getStanzaId());
setFrom(request.getTo());
setTo(request.getFrom());
setType(Type.result);
}
/** /**
* Convenience method to create a new empty {@link Type#result IQ.Type.result} * Convenience method to create a new empty {@link Type#result IQ.Type.result}
* IQ based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} * IQ based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}
@ -289,9 +300,7 @@ public abstract class IQ extends Stanza {
* @throws IllegalArgumentException if the IQ stanza(/packet) does not have a type of * @throws IllegalArgumentException if the IQ stanza(/packet) does not have a type of
* {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}. * {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}.
* @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ. * @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ.
* @deprecated use {@link #createErrorResponse(IQ, org.jivesoftware.smack.packet.XMPPError.Builder)} instead.
*/ */
@Deprecated
public static ErrorIQ createErrorResponse(final IQ request, final XMPPError error) { public static ErrorIQ createErrorResponse(final IQ request, final XMPPError error) {
return createErrorResponse(request, XMPPError.getBuilder(error)); return createErrorResponse(request, XMPPError.getBuilder(error));
} }

View File

@ -73,6 +73,8 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* @param type the type. * @param type the type.
*/ */
public Presence(Type type) { public Presence(Type type) {
// Ensure that the stanza ID is set by calling super().
super();
setType(type); setType(type);
} }
@ -85,6 +87,8 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* @param mode the mode type for this presence update. * @param mode the mode type for this presence update.
*/ */
public Presence(Type type, String status, int priority, Mode mode) { public Presence(Type type, String status, int priority, Mode mode) {
// Ensure that the stanza ID is set by calling super().
super();
setType(type); setType(type);
setStatus(status); setStatus(status);
setPriority(priority); setPriority(priority);

View File

@ -23,6 +23,7 @@ import java.text.ParseException;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import org.jivesoftware.smack.SmackException;
import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.Jid; import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.impl.JidCreate;
@ -122,6 +123,14 @@ public class ParserUtils {
} }
} }
public static int getIntegerAttributeOrThrow(XmlPullParser parser, String name, String throwMessage) throws SmackException {
Integer res = getIntegerAttribute(parser, name);
if (res == null) {
throw new SmackException(throwMessage);
}
return res;
}
public static Integer getIntegerAttribute(XmlPullParser parser, String name) { public static Integer getIntegerAttribute(XmlPullParser parser, String name) {
String valueString = parser.getAttributeValue("", name); String valueString = parser.getAttributeValue("", name);
if (valueString == null) if (valueString == null)

View File

@ -238,6 +238,10 @@ public class XmlStringBuilder implements Appendable, CharSequence {
return this; return this;
} }
public XmlStringBuilder attribute(String name, boolean bool) {
return attribute(name, Boolean.toString(bool));
}
/** /**
* Add a new attribute to this builder, with the {@link java.util.Date} instance as its value, * Add a new attribute to this builder, with the {@link java.util.Date} instance as its value,
* which will get formated with {@link XmppDateTime#formatXEP0082Date(Date)}. * which will get formated with {@link XmppDateTime#formatXEP0082Date(Date)}.

View File

@ -0,0 +1,28 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot;
import org.jivesoftware.smack.SmackException;
public class IoTException extends SmackException {
/**
*
*/
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,42 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot;
import java.util.Map;
import java.util.WeakHashMap;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.XMPPConnection;
public final class IoTManager extends Manager {
private static final Map<XMPPConnection, IoTManager> INSTANCES = new WeakHashMap<>();
public static synchronized IoTManager getInstanceFor(XMPPConnection connection) {
IoTManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new IoTManager(connection);
INSTANCES.put(connection, manager);
}
return manager;
}
private IoTManager(XMPPConnection connection) {
super(connection);
}
}

View File

@ -0,0 +1,161 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import org.jivesoftware.smackx.iot.control.ThingControlRequest;
import org.jivesoftware.smackx.iot.data.ThingMomentaryReadOutRequest;
import org.jivesoftware.smackx.iot.discovery.element.Tag;
import org.jivesoftware.smackx.iot.discovery.element.Tag.Type;
import org.jivesoftware.smackx.iot.element.NodeInfo;
public final class Thing {
private final HashMap<String, Tag> metaTags;
private final boolean selfOwned;
private final NodeInfo nodeInfo;
private final ThingMomentaryReadOutRequest momentaryReadOutRequestHandler;
private final ThingControlRequest controlRequestHandler;
private Thing(Builder builder) {
this.metaTags = builder.metaTags;
this.selfOwned = builder.selfOwned;
this.nodeInfo = builder.nodeInfo;
this.momentaryReadOutRequestHandler = builder.momentaryReadOutRequest;
this.controlRequestHandler = builder.controlRequest;
}
public Collection<Tag> getMetaTags() {
return metaTags.values();
}
public boolean isSelfOwened() {
return selfOwned;
}
public NodeInfo getNodeInfo() {
return nodeInfo;
}
public String getNodeId() {
return nodeInfo.getNodeId();
}
public String getSourceId() {
return nodeInfo.getSourceId();
}
public String getCacheType() {
return nodeInfo.getCacheType();
}
public ThingMomentaryReadOutRequest getMomentaryReadOutRequestHandler() {
return momentaryReadOutRequestHandler;
}
public ThingControlRequest getControlRequestHandler() {
return controlRequestHandler;
}
private String toStringCache;
@Override
public String toString() {
if (toStringCache == null) {
StringBuilder sb = new StringBuilder();
sb.append( "Thing " + nodeInfo + " [");
Iterator<Tag> it = metaTags.values().iterator();
while (it.hasNext()) {
Tag tag = it.next();
sb.append(tag);
if (it.hasNext()) {
sb.append(' ');
}
}
sb.append(']');
toStringCache = sb.toString();
}
return toStringCache;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private HashMap<String, Tag> metaTags = new HashMap<>();
private boolean selfOwned;
private NodeInfo nodeInfo = NodeInfo.EMPTY;
private ThingMomentaryReadOutRequest momentaryReadOutRequest;
private ThingControlRequest controlRequest;
public Builder setSerialNumber(String sn) {
final String name = "SN";
Tag tag = new Tag(name, Type.str, sn);
metaTags.put(name, tag);
return this;
}
public Builder setKey(String key) {
final String name = "KEY";
Tag tag = new Tag(name, Type.str, key);
metaTags.put(name, tag);
return this;
}
public Builder setManufacturer(String manufacturer) {
final String name = "MAN";
Tag tag = new Tag(name, Type.str, manufacturer);
metaTags.put(name, tag);
return this;
}
public Builder setModel(String model) {
final String name = "MODEL";
Tag tag = new Tag(name, Type.str, model);
metaTags.put(name, tag);
return this;
}
public Builder setVersion(String version) {
final String name = "V";
Tag tag = new Tag(name, Type.num, version);
metaTags.put(name, tag);
return this;
}
public Builder setMomentaryReadOutRequestHandler(ThingMomentaryReadOutRequest momentaryReadOutRequestHandler) {
this.momentaryReadOutRequest = momentaryReadOutRequestHandler;
return this;
}
public Builder setControlRequestHandler(ThingControlRequest controlRequest) {
this.controlRequest = controlRequest;
return this;
}
public Thing build() {
return new Thing(this);
}
}
}

View File

@ -0,0 +1,149 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.control;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.iot.Thing;
import org.jivesoftware.smackx.iot.control.element.IoTSetRequest;
import org.jivesoftware.smackx.iot.control.element.IoTSetResponse;
import org.jivesoftware.smackx.iot.control.element.SetData;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jxmpp.jid.FullJid;
/**
* A manger for XEP-0325: Internet of Things - Control.
*
* @author Florian Schmaus {@literal <flo@geekplace.eu>}
* @see <a href="http://xmpp.org/extensions/xep-0325.html">XEP-0323: Internet of Things - Control</a>
*/
public final class IoTControlManager extends Manager {
private static final Map<XMPPConnection, IoTControlManager> INSTANCES = new WeakHashMap<>();
/**
* Get the manger instance responsible for the given connection.
*
* @param connection the XMPP connection.
* @return a manager instance.
*/
public static synchronized IoTControlManager getInstanceFor(XMPPConnection connection) {
IoTControlManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new IoTControlManager(connection);
INSTANCES.put(connection, manager);
}
return manager;
}
private final Map<NodeInfo, Thing> things = new ConcurrentHashMap<>();
private IoTControlManager(XMPPConnection connection) {
super(connection);
connection.registerIQRequestHandler(new AbstractIqRequestHandler(IoTSetRequest.ELEMENT, IoTSetRequest.NAMESPACE, IQ.Type.set, Mode.async) {
@Override
public IQ handleIQRequest(IQ iqRequest) {
// TODO Lookup thing and provide data.
IoTSetRequest iotSetRequest = (IoTSetRequest) iqRequest;
// TODO Add support for multiple things(/NodeInfos).
final Thing thing = things.get(NodeInfo.EMPTY);
if (thing == null) {
// TODO return error if not at least one thing registered.
return null;
}
ThingControlRequest controlRequest = thing.getControlRequestHandler();
if (controlRequest == null) {
// TODO return error if no request handler for things.
return null;
}
try {
controlRequest.processRequest(iotSetRequest.getFrom(), iotSetRequest.getSetData());
} catch (XMPPErrorException e) {
return IQ.createErrorResponse(iotSetRequest, e.getXMPPError());
}
return new IoTSetResponse(iotSetRequest);
}
});
}
/**
* Control a thing by sending a collection of {@link SetData} instructions.
*
* @param jid
* @param data
* @return
* @throws NoResponseException
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
* @see #setUsingIq(FullJid, Collection)
*/
public IoTSetResponse setUsingIq(FullJid jid, SetData data) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
return setUsingIq(jid, Collections.singleton(data));
}
/**
* Control a thing by sending a collection of {@link SetData} instructions.
*
* @param jid the thing to control.
* @param data a collection of {@link SetData} instructions.
* @return the {@link IoTSetResponse} if successful.
* @throws NoResponseException
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
*/
public IoTSetResponse setUsingIq(FullJid jid, Collection<SetData> data) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
IoTSetRequest request = new IoTSetRequest(data);
request.setTo(jid);
IoTSetResponse response = connection().createPacketCollectorAndSend(request).nextResultOrThrow();
return response;
}
/**
* Install a thing in the manager. Activates control functionality (if provided by the thing).
*
* @param thing the thing to install.
*/
public void installThing(Thing thing) {
things.put(thing.getNodeInfo(), thing);
}
public Thing uninstallThing(Thing thing) {
return uninstallThing(thing.getNodeInfo());
}
public Thing uninstallThing(NodeInfo nodeInfo) {
return things.remove(nodeInfo);
}
}

View File

@ -0,0 +1,29 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.control;
import java.util.Collection;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smackx.iot.control.element.SetData;
import org.jxmpp.jid.Jid;
public interface ThingControlRequest {
public void processRequest(Jid from, Collection<SetData> setData) throws XMPPErrorException;
}

View File

@ -0,0 +1,23 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.control.element;
public class Constants {
public static final String IOT_CONTROL_NAMESPACE = "urn:xmpp:iot:control";
}

View File

@ -0,0 +1,48 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.control.element;
import java.util.Collection;
import java.util.Collections;
import org.jivesoftware.smack.packet.IQ;
public class IoTSetRequest extends IQ {
public static final String ELEMENT = "set";
public static final String NAMESPACE = Constants.IOT_CONTROL_NAMESPACE;
private final Collection<SetData> setData;
public IoTSetRequest(Collection<SetData> setData) {
super(ELEMENT, NAMESPACE);
setType(Type.set);
this.setData = Collections.unmodifiableCollection(setData);
}
public Collection<SetData> getSetData() {
return setData;
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.rightAngleBracket();
xml.append(setData);
return xml;
}
}

View File

@ -0,0 +1,41 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.control.element;
import org.jivesoftware.smack.packet.IQ;
public class IoTSetResponse extends IQ {
public static final String ELEMENT = "setResponse";
public static final String NAMESPACE = Constants.IOT_CONTROL_NAMESPACE;
public IoTSetResponse() {
super(ELEMENT, NAMESPACE);
}
public IoTSetResponse(IoTSetRequest iotSetRequest) {
this();
initialzeAsResultFor(iotSetRequest);
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.setEmptyElement();
return xml;
}
}

View File

@ -0,0 +1,38 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.control.element;
public class SetBoolData extends SetData {
public SetBoolData(String name, boolean value) {
this(name, Boolean.toString(value));
booleanCache = value;
}
protected SetBoolData(String name, String value) {
super(name, Type.BOOL, value);
}
private Boolean booleanCache;
public Boolean getBooleanValue() {
if (booleanCache != null) {
booleanCache = Boolean.valueOf(getValue());
}
return booleanCache;
}
}

View File

@ -0,0 +1,92 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.control.element;
import java.util.Locale;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.util.XmlStringBuilder;
public abstract class SetData implements NamedElement {
public enum Type {
BOOL,
INT,
LONG,
DOUBLE,
;
private String toStringCache;
private Type() {
toStringCache = this.name().toLowerCase(Locale.US);
}
@Override
public String toString() {
return toStringCache;
}
}
protected SetData(String name, Type type, String value) {
this.name = name;
this.type = type;
this.value = value;
}
private final String name;
private final Type type;
private final String value;
public final String getName() {
return name;
}
public final String getValue() {
return value;
}
public final Type getType() {
return type;
}
/**
* Returns the root element name.
*
* @return the element name.
*/
@Override
public final String getElementName() {
return getType().toString();
}
/**
* Returns the XML representation of this Element.
*
* @return the stanza(/packet) extension as XML.
*/
@Override
public final XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this);
xml.attribute("name", name);
xml.attribute("value", value);
xml.closeEmptyElement();
return xml;
}
}

View File

@ -0,0 +1,38 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.control.element;
public class SetDoubleData extends SetData {
public SetDoubleData(String name, double value) {
this(name, Double.toString(value));
doubleCache = value;
}
protected SetDoubleData(String name, String value) {
super(name, Type.DOUBLE, value);
}
private Double doubleCache;
public Double getDoubleValue() {
if (doubleCache != null) {
doubleCache = Double.valueOf(getValue());
}
return doubleCache;
}
}

View File

@ -0,0 +1,38 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.control.element;
public class SetIntData extends SetData {
public SetIntData(String name, int value) {
this(name, Integer.toString(value));
integerCache = value;
}
protected SetIntData(String name, String value) {
super(name, Type.INT, value);
}
private Integer integerCache;
public Integer getIntegerValue() {
if (integerCache != null) {
integerCache = Integer.valueOf(getValue());
}
return integerCache;
}
}

View File

@ -0,0 +1,38 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.control.element;
public class SetLongData extends SetData {
public SetLongData(String name, long value) {
this(name, Long.toString(value));
longCache = value;
}
protected SetLongData(String name, String value) {
super(name, Type.LONG, value);
}
private Long longCache;
public Long getLongValue() {
if (longCache != null) {
longCache = Long.valueOf(getValue());
}
return longCache;
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.control.element;

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.control;

View File

@ -0,0 +1,82 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.control.provider;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smackx.iot.control.element.IoTSetRequest;
import org.jivesoftware.smackx.iot.control.element.SetBoolData;
import org.jivesoftware.smackx.iot.control.element.SetData;
import org.jivesoftware.smackx.iot.control.element.SetDoubleData;
import org.jivesoftware.smackx.iot.control.element.SetIntData;
import org.jivesoftware.smackx.iot.control.element.SetLongData;
import org.xmlpull.v1.XmlPullParser;
public class IoTSetRequestProvider extends IQProvider<IoTSetRequest> {
@Override
public IoTSetRequest parse(XmlPullParser parser, int initialDepth) throws Exception {
List<SetData> data = new ArrayList<>(4);
outerloop: while (true) {
final int eventType = parser.next();
final String name = parser.getName();
switch (eventType) {
case XmlPullParser.START_TAG:
switch (name) {
case "bool": {
String valueName = parser.getAttributeValue(null, "name");
String valueString = parser.getAttributeValue(null, "value");
boolean value = Boolean.parseBoolean(valueString);
data.add(new SetBoolData(valueName, value));
}
break;
case "double": {
String valueName = parser.getAttributeValue(null, "name");
String valueString = parser.getAttributeValue(null, "value");
double value = Double.parseDouble(valueString);
data.add(new SetDoubleData(valueName, value));
}
break;
case "int": {
String valueName = parser.getAttributeValue(null, "name");
String valueString = parser.getAttributeValue(null, "value");
int value = Integer.parseInt(valueString);
data.add(new SetIntData(valueName, value));
}
break;
case "long": {
String valueName = parser.getAttributeValue(null, "name");
String valueString = parser.getAttributeValue(null, "value");
long value = Long.parseLong(valueString);
data.add(new SetLongData(valueName, value));
}
break;
}
break;
case XmlPullParser.END_TAG:
if (parser.getDepth() == initialDepth) {
break outerloop;
}
break;
}
}
return new IoTSetRequest(data);
}
}

View File

@ -0,0 +1,30 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.control.provider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smackx.iot.control.element.IoTSetResponse;
import org.xmlpull.v1.XmlPullParser;
public class IoTSetResponseProvider extends IQProvider<IoTSetResponse> {
@Override
public IoTSetResponse parse(XmlPullParser parser, int initialDepth) throws Exception {
return new IoTSetResponse();
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.control.provider;

View File

@ -0,0 +1,210 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.data;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.iot.Thing;
import org.jivesoftware.smackx.iot.data.element.IoTDataField;
import org.jivesoftware.smackx.iot.data.element.IoTDataReadOutAccepted;
import org.jivesoftware.smackx.iot.data.element.IoTDataRequest;
import org.jivesoftware.smackx.iot.data.element.IoTFieldsExtension;
import org.jivesoftware.smackx.iot.data.filter.IoTFieldsExtensionFilter;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jxmpp.jid.EntityFullJid;
/**
* A manager for XEP-0323: Internet of Things - Sensor Data.
*
* @author Florian Schmaus {@literal <flo@geekplace.eu>}
* @see <a href="http://xmpp.org/extensions/xep-0323.html">XEP-0323: Internet of Things - Sensor Data</a>
*/
public final class IoTDataManager extends Manager {
private static final Logger LOGGER = Logger.getLogger(IoTDataManager.class.getName());
private static final Map<XMPPConnection, IoTDataManager> INSTANCES = new WeakHashMap<>();
// Ensure a IoTDataManager exists for every connection.
static {
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
public void connectionCreated(XMPPConnection connection) {
getInstanceFor(connection);
}
});
}
/**
* Get the manger instance responsible for the given connection.
*
* @param connection the XMPP connection.
* @return a manager instance.
*/
public static synchronized IoTDataManager getInstanceFor(XMPPConnection connection) {
IoTDataManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new IoTDataManager(connection);
INSTANCES.put(connection, manager);
}
return manager;
}
private final AtomicInteger nextSeqNr = new AtomicInteger();
private final Map<NodeInfo, Thing> things = new ConcurrentHashMap<>();
private IoTDataManager(XMPPConnection connection) {
super(connection);
connection.registerIQRequestHandler(new AbstractIqRequestHandler(IoTDataRequest.ELEMENT,
IoTDataRequest.NAMESPACE, IQ.Type.get, Mode.async) {
@Override
public IQ handleIQRequest(IQ iqRequest) {
// TODO Verify that iqRequest.from is friend?
final IoTDataRequest dataRequest = (IoTDataRequest) iqRequest;
if (!dataRequest.isMomentary()) {
// TODO return error IQ that non momentary requests are not implemented yet.
return null;
}
// TODO Add support for multiple things(/NodeInfos).
final Thing thing = things.get(NodeInfo.EMPTY);
if (thing == null) {
// TODO return error if not at least one thing registered.
return null;
}
ThingMomentaryReadOutRequest readOutRequest = thing.getMomentaryReadOutRequestHandler();
if (readOutRequest == null) {
// TODO Thing does not provide momentary read-out
return null;
}
// Callback hell begins here. :) XEP-0323 decouples the read-out results from the IQ result. I'm not
// sure if I would have made the same design decision but the reasons where likely being able to get a
// fast read-out acknowledgement back to the requester even with sensors that take "a long time" to
// read-out their values. I had designed that as special case and made the "results in IQ response" the
// normal case.
readOutRequest.momentaryReadOutRequest(new ThingMomentaryReadOutResult() {
@Override
public void momentaryReadOut(List<? extends IoTDataField> results) {
IoTFieldsExtension iotFieldsExtension = IoTFieldsExtension.buildFor(dataRequest.getSequenceNr(), true, thing.getNodeInfo(), results);
Message message = new Message(dataRequest.getFrom());
message.addExtension(iotFieldsExtension);
try {
connection().sendStanza(message);
}
catch (NotConnectedException | InterruptedException e) {
LOGGER.log(Level.SEVERE, "Could not send read-out response " + message, e);
}
}
});
return new IoTDataReadOutAccepted(dataRequest);
}
});
}
/**
* Install a thing in the manager. Activates data read out functionality (if provided by the
* thing).
*
* @param thing the thing to install.
*/
public void installThing(Thing thing) {
things.put(thing.getNodeInfo(), thing);
}
public Thing uninstallThing(Thing thing) {
return uninstallThing(thing.getNodeInfo());
}
public Thing uninstallThing(NodeInfo nodeInfo) {
return things.remove(nodeInfo);
}
/**
* Try to read out a things momentary values.
*
* @param jid the full JID of the thing to read data from.
* @return a list with the read out data.
* @throws NoResponseException
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
*/
public List<IoTFieldsExtension> requestMomentaryValuesReadOut(EntityFullJid jid)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
final XMPPConnection connection = connection();
final int seqNr = nextSeqNr.incrementAndGet();
IoTDataRequest iotDataRequest = new IoTDataRequest(seqNr, true);
iotDataRequest.setTo(jid);
StanzaFilter doneFilter = new IoTFieldsExtensionFilter(seqNr, true);
StanzaFilter dataFilter = new IoTFieldsExtensionFilter(seqNr, false);
// Setup the IoTFieldsExtension message collectors before sending the IQ to avoid a data race.
PacketCollector doneCollector = connection.createPacketCollector(doneFilter);
PacketCollector.Configuration dataCollectorConfiguration = PacketCollector.newConfiguration().setStanzaFilter(
dataFilter).setCollectorToReset(doneCollector);
PacketCollector dataCollector = connection.createPacketCollector(dataCollectorConfiguration);
try {
connection.createPacketCollectorAndSend(iotDataRequest).nextResultOrThrow();
// Wait until a message with an IoTFieldsExtension and the done flag comes in.
doneCollector.nextResult();
}
finally {
// Ensure that the two collectors are canceled in any case.
dataCollector.cancel();
doneCollector.cancel();
}
int collectedCount = dataCollector.getCollectedCount();
List<IoTFieldsExtension> res = new ArrayList<>(collectedCount);
for (int i = 0; i < collectedCount; i++) {
Message message = dataCollector.pollResult();
IoTFieldsExtension iotFieldsExtension = IoTFieldsExtension.from(message);
res.add(iotFieldsExtension);
}
return res;
}
}

View File

@ -0,0 +1,23 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.data;
public interface ThingMomentaryReadOutRequest {
public void momentaryReadOutRequest(ThingMomentaryReadOutResult callback);
}

View File

@ -0,0 +1,27 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.data;
import java.util.List;
import org.jivesoftware.smackx.iot.data.element.IoTDataField;
public interface ThingMomentaryReadOutResult {
public void momentaryReadOut(List<? extends IoTDataField> results);
}

View File

@ -0,0 +1,23 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.data.element;
public class Constants {
public static final String IOT_SENSORDATA_NAMESPACE = "urn:xmpp:iot:data";
}

View File

@ -0,0 +1,111 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.data.element;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.util.XmlStringBuilder;
public abstract class IoTDataField implements NamedElement {
enum Type {
integer("int"),
bool("boolean"),
;
Type(String stringRepresentation) {
this.stringRepresentation = stringRepresentation;
}
private final String stringRepresentation;
}
private final Type type;
private final String name;
protected IoTDataField(Type type, String name) {
this.type = type;
this.name = name;
}
public final String getName() {
return name;
}
@Override
public final String getElementName() {
return type.stringRepresentation;
}
@Override
public final XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this);
xml.attribute("name", name).attribute("value", getValueString());
// TODO handle 'unit' attribute as special case if <numeric/> is implemented.
xml.closeEmptyElement();
return xml;
}
private String valueString;
public final String getValueString() {
if (valueString == null) {
valueString = getValueInternal();
}
return valueString;
}
protected abstract String getValueInternal();
public static class IntField extends IoTDataField {
private final int value;
public IntField(String name, int value) {
super(Type.integer, name);
this.value = value;
}
@Override
protected String getValueInternal() {
return Integer.toString(value);
}
public int getValue() {
return value;
}
}
public static class BooleanField extends IoTDataField {
private final boolean value;
public BooleanField(String name, boolean value) {
super(Type.bool, name);
this.value = value;
}
@Override
protected String getValueInternal() {
return Boolean.toString(value);
}
public boolean getValue() {
return value;
}
}
}

View File

@ -0,0 +1,54 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.data.element;
import org.jivesoftware.smack.packet.IQ;
public class IoTDataReadOutAccepted extends IQ {
public static final String ELEMENT = "accepted";
public static final String NAMESPACE = Constants.IOT_SENSORDATA_NAMESPACE;
/**
* The sequence number. According to XEP-0323 an xs:int.
*/
private final int seqNr;
private final boolean queued;
public IoTDataReadOutAccepted(int seqNr, boolean queued) {
super(ELEMENT, NAMESPACE);
this.seqNr = seqNr;
this.queued = queued;
setType(Type.result);
}
public IoTDataReadOutAccepted(IoTDataRequest dataRequest) {
this(dataRequest.getSequenceNr(), false);
setStanzaId(dataRequest.getStanzaId());
setTo(dataRequest.getFrom());
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.attribute("seqnr", seqNr);
xml.optBooleanAttribute("queued", queued);
xml.setEmptyElement();
return xml;
}
}

View File

@ -0,0 +1,54 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.data.element;
import org.jivesoftware.smack.packet.IQ;
public class IoTDataRequest extends IQ {
public static final String ELEMENT = "req";
public static final String NAMESPACE = Constants.IOT_SENSORDATA_NAMESPACE;
/**
* The sequence nummber. According to XEP-0323 an xs:int.
*/
private final int seqNr;
private final boolean momentary;
public IoTDataRequest(int seqNr, boolean momentary) {
super(ELEMENT, NAMESPACE);
this.seqNr = seqNr;
this.momentary = momentary;
}
public int getSequenceNr() {
return seqNr;
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.attribute("seqnr", seqNr);
xml.optBooleanAttribute("momentary", momentary);
xml.setEmptyElement();
return xml;
}
public boolean isMomentary() {
return momentary;
}
}

View File

@ -0,0 +1,92 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.data.element;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.iot.element.NodeInfo;
public class IoTFieldsExtension implements ExtensionElement {
public static final String ELEMENT = "fields";
public static final String NAMESPACE = Constants.IOT_SENSORDATA_NAMESPACE;
private final int seqNr;
private final boolean done;
private final List<NodeElement> nodes;
public IoTFieldsExtension(int seqNr, boolean done, NodeElement node) {
this(seqNr, done, Collections.singletonList(node));
}
public IoTFieldsExtension(int seqNr, boolean done, List<NodeElement> nodes) {
this.seqNr = seqNr;
this.done = done;
this.nodes = Collections.unmodifiableList(nodes);
}
public int getSequenceNr() {
return seqNr;
}
public boolean isDone() {
return done;
}
public List<NodeElement> getNodes() {
return nodes;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this);
xml.attribute("seqnr", Integer.toString(seqNr));
xml.attribute("done", done);
xml.rightAngleBracket();
xml.append(nodes);
xml.closeElement(this);
return xml;
}
public static IoTFieldsExtension buildFor(int seqNr, boolean done, NodeInfo nodeInfo,
List<? extends IoTDataField> data) {
TimestampElement timestampElement = new TimestampElement(new Date(), data);
NodeElement nodeElement = new NodeElement(nodeInfo, timestampElement);
return new IoTFieldsExtension(seqNr, done, nodeElement);
}
public static IoTFieldsExtension from(Message message) {
return message.getExtension(ELEMENT, NAMESPACE);
}
}

View File

@ -0,0 +1,63 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.data.element;
import java.util.Collections;
import java.util.List;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.iot.element.NodeInfo;
public class NodeElement implements NamedElement {
public static final String ELEMENT = "node";
private final NodeInfo nodeInfo;
private final List<TimestampElement> timestampElements;
public NodeElement(NodeInfo nodeInfo, TimestampElement timestampElement) {
this(nodeInfo, Collections.singletonList(timestampElement));
}
public NodeElement(NodeInfo nodeInfo, List<TimestampElement> timestampElements) {
this.nodeInfo = nodeInfo;
this.timestampElements = Collections.unmodifiableList(timestampElements);
}
public List<TimestampElement> getTimestampElements() {
return timestampElements;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this);
nodeInfo.appendTo(xml);
xml.rightAngleBracket();
xml.append(timestampElements);
xml.closeElement(this);
return xml;
}
}

View File

@ -0,0 +1,59 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.data.element;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class TimestampElement implements NamedElement {
public static final String ELEMENT = "timestamp";
private final Date date;
private final List<? extends IoTDataField> fields;
public TimestampElement(Date date, List<? extends IoTDataField> fields) {
this.date = date;
this.fields = Collections.unmodifiableList(fields);
}
public List<? extends IoTDataField> getDataFields() {
return fields;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this);
xml.attribute("value", date);
xml.rightAngleBracket();
xml.append(fields);
xml.closeElement(this);
return xml;
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.data.element;

View File

@ -0,0 +1,48 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.data.filter;
import org.jivesoftware.smack.filter.FlexibleStanzaTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.iot.data.element.IoTFieldsExtension;
public class IoTFieldsExtensionFilter extends FlexibleStanzaTypeFilter<Message> {
private final int seqNr;
private final boolean onlyDone;
public IoTFieldsExtensionFilter(int seqNr, boolean onlyDone) {
this.seqNr = seqNr;
this.onlyDone = onlyDone;
}
@Override
protected boolean acceptSpecific(Message message) {
IoTFieldsExtension iotFieldsExtension = IoTFieldsExtension.from(message);
if (iotFieldsExtension == null) {
return false;
}
if (iotFieldsExtension.getSequenceNr() != seqNr) {
return false;
}
if (onlyDone && !iotFieldsExtension.isDone()) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.data.filter;

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.data;

View File

@ -0,0 +1,33 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.data.provider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.iot.data.element.IoTDataReadOutAccepted;
import org.xmlpull.v1.XmlPullParser;
public class IoTDataReadOutAcceptedProvider extends IQProvider<IoTDataReadOutAccepted> {
@Override
public IoTDataReadOutAccepted parse(XmlPullParser parser, int initialDepth) throws Exception {
int seqNr = ParserUtils.getIntegerAttributeOrThrow(parser, "seqnr", "IoT data request <accepted/> without sequence number");
boolean queued = ParserUtils.getBooleanAttribute(parser, "queued", false);
return new IoTDataReadOutAccepted(seqNr, queued);
}
}

View File

@ -0,0 +1,33 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.data.provider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.iot.data.element.IoTDataRequest;
import org.xmlpull.v1.XmlPullParser;
public class IoTDataRequestProvider extends IQProvider<IoTDataRequest> {
@Override
public IoTDataRequest parse(XmlPullParser parser, int initialDepth) throws Exception {
int seqNr = ParserUtils.getIntegerAttributeOrThrow(parser, "seqnr", "IoT data request without sequence number");
boolean momentary = ParserUtils.getBooleanAttribute(parser, "momentary", false);
return new IoTDataRequest(seqNr, momentary);
}
}

View File

@ -0,0 +1,136 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.data.provider;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.iot.data.element.IoTDataField;
import org.jivesoftware.smackx.iot.data.element.IoTFieldsExtension;
import org.jivesoftware.smackx.iot.data.element.NodeElement;
import org.jivesoftware.smackx.iot.data.element.TimestampElement;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jivesoftware.smackx.iot.parser.NodeInfoParser;
import org.jxmpp.util.XmppDateTime;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
public class IoTFieldsExtensionProvider extends ExtensionElementProvider<IoTFieldsExtension> {
private static final Logger LOGGER = Logger.getLogger(IoTFieldsExtensionProvider.class.getName());
@Override
public IoTFieldsExtension parse(XmlPullParser parser, int initialDepth) throws Exception {
int seqNr = ParserUtils.getIntegerAttributeOrThrow(parser, "seqnr", "IoT data request <accepted/> without sequence number");
boolean done = ParserUtils.getBooleanAttribute(parser, "done", false);
List<NodeElement> nodes = new ArrayList<>();
outerloop: while (true) {
final int eventType = parser.next();
final String name = parser.getName();
switch (eventType) {
case XmlPullParser.START_TAG:
switch (name) {
case NodeElement.ELEMENT:
NodeElement node = parseNode(parser);
nodes.add(node);
break;
}
break;
case XmlPullParser.END_TAG:
if (parser.getDepth() == initialDepth) {
break outerloop;
}
break;
}
}
return new IoTFieldsExtension(seqNr, done, nodes);
}
public NodeElement parseNode(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {
final int initialDepth = parser.getDepth();
final NodeInfo nodeInfo = NodeInfoParser.parse(parser);
List<TimestampElement> timestampElements = new ArrayList<>();
outerloop: while (true) {
final int eventType = parser.next();
final String name = parser.getName();
switch (eventType) {
case XmlPullParser.START_TAG:
switch (name){
case TimestampElement.ELEMENT:
TimestampElement timestampElement = parseTimestampElement(parser);
timestampElements.add(timestampElement);
break;
}
break;
case XmlPullParser.END_TAG:
if (parser.getDepth() == initialDepth) {
break outerloop;
}
break;
}
}
return new NodeElement(nodeInfo, timestampElements);
}
public TimestampElement parseTimestampElement(XmlPullParser parser) throws ParseException, XmlPullParserException, IOException {
final int initialDepth = parser.getDepth();
final String dateString = parser.getAttributeValue(null, "value");
final Date date = XmppDateTime.parseDate(dateString);
List<IoTDataField> fields = new ArrayList<>();
outerloop: while (true) {
final int eventType = parser.next();
final String name = parser.getName();
switch (eventType) {
case XmlPullParser.START_TAG:
IoTDataField field = null;
final String fieldName = parser.getAttributeValue(null, "name");
final String fieldValue = parser.getAttributeValue(null, "value");
switch (name) {
case "int": {
int value = Integer.parseInt(fieldValue);
field = new IoTDataField.IntField(fieldName, value);
}
break;
case "boolean": {
boolean value = Boolean.parseBoolean(fieldValue);
field = new IoTDataField.BooleanField(fieldName, value);
}
break;
default:
LOGGER.warning("IoT Data field type '" + name + "' not implement yet. Ignoring.");
break;
}
if (field != null) {
fields.add(field);
}
break;
case XmlPullParser.END_TAG:
if (parser.getDepth() == initialDepth) {
break outerloop;
}
break;
}
}
return new TimestampElement(date, fields);
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.data.provider;

View File

@ -0,0 +1,27 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery;
import org.jxmpp.jid.BareJid;
public abstract class AbstractThingStateChangeListener implements ThingStateChangeListener {
@Override
public void owned(BareJid owner) {
}
}

View File

@ -0,0 +1,38 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery;
import org.jivesoftware.smackx.iot.IoTException;
import org.jivesoftware.smackx.iot.discovery.element.IoTClaimed;
public class IoTClaimedException extends IoTException {
/**
*
*/
private static final long serialVersionUID = 1L;
private final IoTClaimed iotClaimed;
public IoTClaimedException(IoTClaimed iotClaimed) {
this.iotClaimed = iotClaimed;
}
public IoTClaimed getIoTClaimed() {
return iotClaimed;
}
}

View File

@ -0,0 +1,420 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
import org.jivesoftware.smackx.iot.Thing;
import org.jivesoftware.smackx.iot.control.IoTControlManager;
import org.jivesoftware.smackx.iot.data.IoTDataManager;
import org.jivesoftware.smackx.iot.discovery.element.Constants;
import org.jivesoftware.smackx.iot.discovery.element.IoTClaimed;
import org.jivesoftware.smackx.iot.discovery.element.IoTDisown;
import org.jivesoftware.smackx.iot.discovery.element.IoTDisowned;
import org.jivesoftware.smackx.iot.discovery.element.IoTMine;
import org.jivesoftware.smackx.iot.discovery.element.IoTRegister;
import org.jivesoftware.smackx.iot.discovery.element.IoTRemove;
import org.jivesoftware.smackx.iot.discovery.element.IoTRemoved;
import org.jivesoftware.smackx.iot.discovery.element.IoTUnregister;
import org.jivesoftware.smackx.iot.discovery.element.Tag;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jivesoftware.smackx.iot.provisioning.IoTProvisioningManager;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
/**
* A manager for XEP-0347: Internet of Things - Discovery. Used to register and discover things.
*
* @author Florian Schmaus {@literal <flo@geekplace.eu>}
* @see <a href="http://xmpp.org/extensions/xep-0347.html">XEP-0347: Internet of Things - Discovery</a>
*
*/
public final class IoTDiscoveryManager extends Manager {
private static final Logger LOGGER = Logger.getLogger(IoTDiscoveryManager.class.getName());
private static final Map<XMPPConnection, IoTDiscoveryManager> INSTANCES = new WeakHashMap<>();
// Ensure a IoTProvisioningManager exists for every connection.
static {
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
public void connectionCreated(XMPPConnection connection) {
getInstanceFor(connection);
}
});
}
/**
* Get the manger instance responsible for the given connection.
*
* @param connection the XMPP connection.
* @return a manager instance.
*/
public static synchronized IoTDiscoveryManager getInstanceFor(XMPPConnection connection) {
IoTDiscoveryManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new IoTDiscoveryManager(connection);
INSTANCES.put(connection, manager);
}
return manager;
}
private Jid preconfiguredRegistry;
/**
* A set of all registries we have interacted so far. {@link #isRegistry(BareJid)} uses this to
* determine if the jid is a registry. Note that we currently do not record which thing
* interacted with which registry. This allows any registry we have interacted so far with, to
* send registry control stanzas about any other thing, and we would process them.
*/
private final Set<Jid> usedRegistries = new HashSet<>();
/**
* Internal state of the things. Uses <code>null</code> for the single thing without node info attached.
*/
private final Map<NodeInfo, ThingState> things = new HashMap<>();
private IoTDiscoveryManager(XMPPConnection connection) {
super(connection);
connection.registerIQRequestHandler(
new AbstractIqRequestHandler(IoTClaimed.ELEMENT, IoTClaimed.NAMESPACE, IQ.Type.set, Mode.sync) {
@Override
public IQ handleIQRequest(IQ iqRequest) {
if (!isRegistry(iqRequest.getFrom())) {
LOGGER.log(Level.SEVERE, "Received control stanza from non-registry entity: " + iqRequest);
return null;
}
IoTClaimed iotClaimed = (IoTClaimed) iqRequest;
Jid owner = iotClaimed.getJid();
NodeInfo nodeInfo = iotClaimed.getNodeInfo();
// Update the state.
ThingState state = getStateFor(nodeInfo);
state.setOwner(owner.asBareJid());
LOGGER.info("Our thing got claimed by " + owner + ". " + iotClaimed);
IoTProvisioningManager iotProvisioningManager = IoTProvisioningManager.getInstanceFor(
connection());
try {
iotProvisioningManager.sendFriendshipRequest(owner.asBareJid());
}
catch (NotConnectedException | InterruptedException e) {
LOGGER.log(Level.WARNING, "Could not friendship owner", e);
}
return IQ.createResultIQ(iqRequest);
}
});
connection.registerIQRequestHandler(new AbstractIqRequestHandler(IoTDisowned.ELEMENT, IoTDisowned.NAMESPACE,
IQ.Type.set, Mode.sync) {
@Override
public IQ handleIQRequest(IQ iqRequest) {
if (!isRegistry(iqRequest.getFrom())) {
LOGGER.log(Level.SEVERE, "Received control stanza from non-registry entity: " + iqRequest);
return null;
}
IoTDisowned iotDisowned = (IoTDisowned) iqRequest;
Jid from = iqRequest.getFrom();
NodeInfo nodeInfo = iotDisowned.getNodeInfo();
ThingState state = getStateFor(nodeInfo);
if (!from.equals(state.getRegistry())) {
LOGGER.severe("Received <disowned/> for " + nodeInfo + " from " + from
+ " but this is not the registry " + state.getRegistry() + " of the thing.");
return null;
}
if (state.isOwned()) {
state.setUnowned();
} else {
LOGGER.fine("Received <disowned/> for " + nodeInfo + " but thing was not owned.");
}
return IQ.createResultIQ(iqRequest);
}
});
// XEP-0347 § 3.9 (ex28-29): <removed/>
connection.registerIQRequestHandler(new AbstractIqRequestHandler(IoTRemoved.ELEMENT, IoTRemoved.NAMESPACE, IQ.Type.set, Mode.async) {
@Override
public IQ handleIQRequest(IQ iqRequest) {
if (!isRegistry(iqRequest.getFrom())) {
LOGGER.log(Level.SEVERE, "Received control stanza from non-registry entity: " + iqRequest);
return null;
}
IoTRemoved iotRemoved = (IoTRemoved) iqRequest;
ThingState state = getStateFor(iotRemoved.getNodeInfo());
state.setRemoved();
// Unfriend registry. "It does this, so the Thing can remove the friendship and stop any
// meta data updates to the Registry."
try {
IoTProvisioningManager.getInstanceFor(connection()).unfriend(iotRemoved.getFrom());
}
catch (NotConnectedException | InterruptedException e) {
LOGGER.log(Level.SEVERE, "Could not unfriend registry after <removed/>", e);
}
return IQ.createResultIQ(iqRequest);
}
});
}
/**
* Try to find an XMPP IoT registry.
*
* @return the JID of a Thing Registry if one could be found.
* @throws InterruptedException
* @throws NotConnectedException
* @throws XMPPErrorException
* @throws NoResponseException
* @see <a href="http://xmpp.org/extensions/xep-0347.html#findingregistry">XEP-0347 § 3.5 Finding Thing Registry</a>
*/
public Jid findRegistry()
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
if (preconfiguredRegistry != null) {
return preconfiguredRegistry;
}
final XMPPConnection connection = connection();
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
List<DiscoverInfo> discoverInfos = sdm.findServicesDiscoverInfo(Constants.IOT_DISCOVERY_NAMESPACE, true, true);
if (!discoverInfos.isEmpty()) {
return discoverInfos.get(0).getFrom();
}
return null;
}
// Thing Registration - XEP-0347 § 3.6 - 3.8
public ThingState registerThing(Thing thing)
throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException, IoTClaimedException {
Jid registry = findRegistry();
return registerThing(registry, thing);
}
public ThingState registerThing(Jid registry, Thing thing)
throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException, IoTClaimedException {
final XMPPConnection connection = connection();
IoTRegister iotRegister = new IoTRegister(thing.getMetaTags(), thing.getNodeInfo(), thing.isSelfOwened());
iotRegister.setTo(registry);
IQ result = connection.createPacketCollectorAndSend(iotRegister).nextResultOrThrow();
if (result instanceof IoTClaimed) {
IoTClaimed iotClaimedResult = (IoTClaimed) result;
throw new IoTClaimedException(iotClaimedResult);
}
ThingState state = getStateFor(thing.getNodeInfo());
state.setRegistry(registry.asBareJid());
interactWithRegistry(registry);
IoTDataManager.getInstanceFor(connection).installThing(thing);
IoTControlManager.getInstanceFor(connection).installThing(thing);
return state;
}
// Thing Claiming - XEP-0347 § 3.9
public IoTClaimed claimThing(Collection<Tag> metaTags) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
return claimThing(metaTags, true);
}
public IoTClaimed claimThing(Collection<Tag> metaTags, boolean publicThing) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
Jid registry = findRegistry();
return claimThing(registry, metaTags, publicThing);
}
/**
* Claim a thing by providing a collection of meta tags. If the claim was successful, then a {@link IoTClaimed}
* instance will be returned, which contains the XMPP address of the thing. Use {@link IoTClaimed#getJid()} to
* retrieve this address.
*
* @param registry the registry use to claim the thing.
* @param metaTags a collection of meta tags used to identify the thing.
* @param publicThing if this is a public thing.
* @return a {@link IoTClaimed} if successful.
* @throws NoResponseException
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
*/
public IoTClaimed claimThing(Jid registry, Collection<Tag> metaTags, boolean publicThing) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
interactWithRegistry(registry);
IoTMine iotMine = new IoTMine(metaTags, publicThing);
iotMine.setTo(registry);
IoTClaimed iotClaimed = connection().createPacketCollectorAndSend(iotMine).nextResultOrThrow();
// The 'jid' attribute of the <claimed/> response now represents the XMPP address of the thing we just successfully claimed.
Jid thing = iotClaimed.getJid();
IoTProvisioningManager.getInstanceFor(connection()).sendFriendshipRequest(thing.asBareJid());
return iotClaimed;
}
// Thing Removal - XEP-0347 § 3.10
public void removeThing(BareJid thing)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
removeThing(thing, NodeInfo.EMPTY);
}
public void removeThing(BareJid thing, NodeInfo nodeInfo)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
Jid registry = findRegistry();
removeThing(registry, thing, nodeInfo);
}
public void removeThing(Jid registry, BareJid thing, NodeInfo nodeInfo)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
interactWithRegistry(registry);
IoTRemove iotRemove = new IoTRemove(thing, nodeInfo);
iotRemove.setTo(registry);
connection().createPacketCollectorAndSend(iotRemove).nextResultOrThrow();
// We no not update the ThingState here, as this is done in the <removed/> IQ handler above.;
}
// Thing Unregistering - XEP-0347 § 3.16
public void unregister()
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
unregister(NodeInfo.EMPTY);
}
public void unregister(NodeInfo nodeInfo)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
Jid registry = findRegistry();
unregister(registry, nodeInfo);
}
public void unregister(Jid registry, NodeInfo nodeInfo)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
interactWithRegistry(registry);
IoTUnregister iotUnregister = new IoTUnregister(nodeInfo);
iotUnregister.setTo(registry);
connection().createPacketCollectorAndSend(iotUnregister).nextResultOrThrow();
ThingState state = getStateFor(nodeInfo);
state.setUnregistered();
final XMPPConnection connection = connection();
IoTDataManager.getInstanceFor(connection).uninstallThing(nodeInfo);
IoTControlManager.getInstanceFor(connection).uninstallThing(nodeInfo);
}
// Thing Disowning - XEP-0347 § 3.17
public void disownThing(Jid thing)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
disownThing(thing, NodeInfo.EMPTY);
}
public void disownThing(Jid thing, NodeInfo nodeInfo)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
Jid registry = findRegistry();
disownThing(registry, thing, nodeInfo);
}
public void disownThing(Jid registry, Jid thing, NodeInfo nodeInfo)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
interactWithRegistry(registry);
IoTDisown iotDisown = new IoTDisown(thing, nodeInfo);
iotDisown.setTo(registry);
connection().createPacketCollectorAndSend(iotDisown).nextResultOrThrow();
}
// Registry utility methods
public boolean isRegistry(BareJid jid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
// At some point 'usedRegistries' will also contain the registry returned by findRegistry(), but since this is
// not the case from the beginning, we perform findRegistry().equals(jid) too.
if (findRegistry().equals(jid)) {
return true;
}
if (usedRegistries.contains(jid)) {
return true;
}
return false;
}
public boolean isRegistry(Jid jid) {
try {
return isRegistry(jid.asBareJid());
}
catch (NoResponseException | XMPPErrorException | NotConnectedException
| InterruptedException e) {
LOGGER.log(Level.WARNING, "Could not determine if " + jid + " is a registry", e);
return false;
}
}
private void interactWithRegistry(Jid registry) throws NotConnectedException, InterruptedException {
boolean isNew = usedRegistries.add(registry);
if (!isNew) {
return;
}
IoTProvisioningManager iotProvisioningManager = IoTProvisioningManager.getInstanceFor(connection());
iotProvisioningManager.sendFriendshipRequestIfRequired(registry.asBareJid());
}
public ThingState getStateFor(Thing thing) {
return things.get(thing.getNodeInfo());
}
private ThingState getStateFor(NodeInfo nodeInfo) {
ThingState state = things.get(nodeInfo);
if (state == null) {
state = new ThingState(nodeInfo);
things.put(nodeInfo, state);
}
return state;
}
}

View File

@ -0,0 +1,95 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jxmpp.jid.BareJid;
public class ThingState {
private final NodeInfo nodeInfo;
private BareJid registry;
private BareJid owner;
private boolean removed;
private final List<ThingStateChangeListener> listeners = new CopyOnWriteArrayList<>();
ThingState(NodeInfo nodeInfo) {
this.nodeInfo = nodeInfo;
}
void setRegistry(BareJid registry) {
this.registry = registry;
}
void setUnregistered() {
this.registry = null;
}
void setOwner(final BareJid owner) {
this.owner = owner;
Async.go(new Runnable() {
@Override
public void run() {
for (ThingStateChangeListener thingStateChangeListener : listeners) {
thingStateChangeListener.owned(owner);
}
}
});
}
void setUnowned() {
this.owner = null;
}
void setRemoved() {
removed = true;
}
public NodeInfo getNodeInfo() {
return nodeInfo;
}
public BareJid getRegistry() {
return registry;
}
public BareJid getOwner() {
return owner;
}
public boolean isOwned() {
return owner != null;
}
public boolean isRemoved() {
return removed;
}
public boolean setThingStateChangeListener(ThingStateChangeListener thingStateChangeListener) {
return listeners.add(thingStateChangeListener);
}
public boolean removeThingStateChangeListener(ThingStateChangeListener thingStateChangeListener) {
return listeners.remove(thingStateChangeListener);
}
}

View File

@ -0,0 +1,25 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery;
import org.jxmpp.jid.BareJid;
public interface ThingStateChangeListener {
public void owned(BareJid owner);
}

View File

@ -0,0 +1,23 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.element;
public class Constants {
public static final String IOT_DISCOVERY_NAMESPACE = "urn:xmpp:iot:discovery";
}

View File

@ -0,0 +1,66 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.element;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jxmpp.jid.Jid;
public class IoTClaimed extends IQ {
public static final String ELEMENT = "claimed";
public static final String NAMESPACE = Constants.IOT_DISCOVERY_NAMESPACE;
private final Jid jid;
private final NodeInfo nodeInfo;
public IoTClaimed(Jid jid) {
this(jid, NodeInfo.EMPTY);
}
public IoTClaimed(Jid jid, NodeInfo nodeInfo) {
super(ELEMENT, NAMESPACE);
this.jid = jid;
this.nodeInfo = nodeInfo;
}
public Jid getJid() {
return jid;
}
public String getNodeId() {
return nodeInfo.getNodeId();
}
public String getSourceId() {
return nodeInfo.getSourceId();
}
public NodeInfo getNodeInfo() {
return nodeInfo;
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.attribute("jid", jid);
nodeInfo.appendTo(xml);
xml.setEmptyElement();
return xml;
}
}

View File

@ -0,0 +1,62 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.element;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jxmpp.jid.Jid;
public class IoTDisown extends IQ {
public static final String ELEMENT = "disown";
public static final String NAMESPACE = Constants.IOT_DISCOVERY_NAMESPACE;
private final Jid jid;
private final NodeInfo nodeInfo;
public IoTDisown(Jid jid) {
this(jid, NodeInfo.EMPTY);
}
public IoTDisown(Jid jid, NodeInfo nodeInfo) {
super(ELEMENT, NAMESPACE);
this.jid = jid;
this.nodeInfo = nodeInfo;
}
public Jid getJid() {
return jid;
}
public String getNodeId() {
return nodeInfo != null ? nodeInfo.getNodeId() : null;
}
public String getSourceId() {
return nodeInfo != null ? nodeInfo.getSourceId() : null;
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.attribute("jid", jid);
nodeInfo.appendTo(xml);
xml.rightAngleBracket();
return xml;
}
}

View File

@ -0,0 +1,53 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.element;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.iot.element.NodeInfo;
public class IoTDisowned extends IQ {
public static final String ELEMENT = "disown";
public static final String NAMESPACE = Constants.IOT_DISCOVERY_NAMESPACE;
private final NodeInfo nodeInfo;
public IoTDisowned(NodeInfo nodeInfo) {
super(ELEMENT, NAMESPACE);
this.nodeInfo = nodeInfo;
}
public String getNodeId() {
return nodeInfo.getNodeId();
}
public String getSourceId() {
return nodeInfo.getSourceId();
}
public NodeInfo getNodeInfo() {
return nodeInfo;
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
nodeInfo.appendTo(xml);
xml.setEmptyElement();
return xml;
}
}

View File

@ -0,0 +1,52 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.element;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.jivesoftware.smack.packet.IQ;
public class IoTMine extends IQ {
public static final String ELEMENT = "mine";
public static final String NAMESPACE = Constants.IOT_DISCOVERY_NAMESPACE;
private final List<Tag> metaTags;
private final boolean publicThing;
public IoTMine(Collection<Tag> metaTags, boolean publicThing) {
this(new ArrayList<>(metaTags), publicThing);
}
public IoTMine(List<Tag> metaTags, boolean publicThing) {
super(ELEMENT, NAMESPACE);
this.metaTags = metaTags;
this.publicThing = publicThing;
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.optBooleanAttributeDefaultTrue("public", publicThing);
xml.rightAngleBracket();
xml.append(metaTags);
return xml;
}
}

View File

@ -0,0 +1,54 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.element;
import java.util.Collection;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.iot.element.NodeInfo;
public class IoTRegister extends IQ {
public static final String ELEMENT = "register";
public static final String NAMESPACE = Constants.IOT_DISCOVERY_NAMESPACE;
private final Collection<Tag> tags;
private final NodeInfo nodeInfo;
private final boolean selfOwned;
public IoTRegister(Collection<Tag> tags, NodeInfo nodeInfo, boolean selfOwned) {
super(ELEMENT, NAMESPACE);
if (tags.isEmpty()) {
throw new IllegalArgumentException();
}
this.tags = tags;
this.nodeInfo = nodeInfo;
this.selfOwned = selfOwned;
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
nodeInfo.appendTo(xml);
xml.optBooleanAttribute("selfOwned", selfOwned);
xml.rightAngleBracket();
xml.append(tags);
return xml;
}
}

View File

@ -0,0 +1,67 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.element;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
public class IoTRemove extends IQ {
public static final String ELEMENT = "remove";
public static final String NAMESPACE = Constants.IOT_DISCOVERY_NAMESPACE;
/**
* The XMPP address of the Thing to be removed from the registry. According to XEP-0347 § 3.10 the
* "resource-less JID of the Thing" has to be used, therefore we use {@link BareJid} here.
*/
private final BareJid jid;
private final NodeInfo nodeInfo;
public IoTRemove(BareJid jid) {
this(jid, NodeInfo.EMPTY);
}
public IoTRemove(BareJid jid, NodeInfo nodeInfo) {
super(ELEMENT, NAMESPACE);
this.jid = jid;
this.nodeInfo = nodeInfo;
}
public Jid getJid() {
return jid;
}
public String getNodeId() {
return nodeInfo != null ? nodeInfo.getNodeId() : null;
}
public String getSourceId() {
return nodeInfo != null ? nodeInfo.getSourceId() : null;
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.attribute("jid", jid);
nodeInfo.appendTo(xml);
xml.setEmptyElement();
return xml;
}
}

View File

@ -0,0 +1,57 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.element;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.iot.element.NodeInfo;
public class IoTRemoved extends IQ {
public static final String ELEMENT = "removed";
public static final String NAMESPACE = Constants.IOT_DISCOVERY_NAMESPACE;
private final NodeInfo nodeInfo;
public IoTRemoved() {
this(NodeInfo.EMPTY);
}
public IoTRemoved(NodeInfo nodeInfo) {
super(ELEMENT, NAMESPACE);
this.nodeInfo = nodeInfo;
}
public String getNodeId() {
return nodeInfo.getNodeId();
}
public String getSourceId() {
return nodeInfo.getSourceId();
}
public NodeInfo getNodeInfo() {
return nodeInfo;
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
nodeInfo.appendTo(xml);
xml.setEmptyElement();
return xml;
}
}

View File

@ -0,0 +1,41 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.element;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.iot.element.NodeInfo;
public class IoTUnregister extends IQ {
public static final String ELEMENT = "unregister";
public static final String NAMESPACE = Constants.IOT_DISCOVERY_NAMESPACE;
private final NodeInfo nodeInfo;
public IoTUnregister(NodeInfo nodeInfo) {
super(ELEMENT, NAMESPACE);
this.nodeInfo = nodeInfo;
setType(Type.set);
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
nodeInfo.appendTo(xml);
xml.rightAngleBracket();
return xml;
}
}

View File

@ -0,0 +1,78 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.discovery.element;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class Tag implements NamedElement {
public enum Type {
str,
num;
}
private final String name;
private final Type type;
private final String value;
public Tag(String name, Type type, String value) {
// TODO According to XEP-0347 § 5.2 names are case insensitive. Uppercase them all?
this.name = StringUtils.requireNotNullOrEmpty(name, "name must not be null or empty");
this.type = Objects.requireNonNull(type);
this.value = StringUtils.requireNotNullOrEmpty(value, "value must not be null or empty");
if (this.name.length() > 32) {
throw new IllegalArgumentException("Meta Tag names must not be longer then 32 characters (XEP-0347 § 5.2");
}
if (this.type == Type.str && this.value.length() > 128) {
throw new IllegalArgumentException("Meta Tag string values must not be longer then 128 characters (XEP-0347 § 5.2");
}
}
public String getName() {
return name;
}
public Type getType() {
return type;
}
public String getValue() {
return value;
}
@Override
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this);
xml.attribute("name", name);
xml.attribute("value", value);
xml.closeEmptyElement();
return xml;
}
@Override
public String getElementName() {
return getType().toString();
}
@Override
public String toString() {
return name + '(' + type + "):" + value;
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.discovery.element;

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT XEP-0347 Discovery. See {@link org.jivesoftware.smackx.iot.discovery.IoTDiscoveryManager}.
*/
package org.jivesoftware.smackx.iot.discovery;

View File

@ -0,0 +1,36 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.provider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.iot.discovery.element.IoTClaimed;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jivesoftware.smackx.iot.parser.NodeInfoParser;
import org.jxmpp.jid.Jid;
import org.xmlpull.v1.XmlPullParser;
public class IoTClaimedProvider extends IQProvider<IoTClaimed> {
@Override
public IoTClaimed parse(XmlPullParser parser, int initialDepth) throws Exception {
Jid jid = ParserUtils.getJidAttribute(parser);
NodeInfo nodeInfo = NodeInfoParser.parse(parser);
return new IoTClaimed(jid, nodeInfo);
}
}

View File

@ -0,0 +1,36 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.provider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.iot.discovery.element.IoTDisown;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jivesoftware.smackx.iot.parser.NodeInfoParser;
import org.jxmpp.jid.Jid;
import org.xmlpull.v1.XmlPullParser;
public class IoTDisownProvider extends IQProvider<IoTDisown> {
@Override
public IoTDisown parse(XmlPullParser parser, int initialDepth) throws Exception {
Jid jid = ParserUtils.getJidAttribute(parser);
NodeInfo nodeInfo = NodeInfoParser.parse(parser);
return new IoTDisown(jid, nodeInfo);
}
}

View File

@ -0,0 +1,33 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.provider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smackx.iot.discovery.element.IoTDisowned;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jivesoftware.smackx.iot.parser.NodeInfoParser;
import org.xmlpull.v1.XmlPullParser;
public class IoTDisownedProvider extends IQProvider<IoTDisowned> {
@Override
public IoTDisowned parse(XmlPullParser parser, int initialDepth) throws Exception {
NodeInfo nodeInfo = NodeInfoParser.parse(parser);
return new IoTDisowned(nodeInfo);
}
}

View File

@ -0,0 +1,62 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.provider;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.iot.discovery.element.IoTRegister;
import org.jivesoftware.smackx.iot.discovery.element.Tag;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jivesoftware.smackx.iot.parser.NodeInfoParser;
import org.xmlpull.v1.XmlPullParser;
public class IoTRegisterProvider extends IQProvider<IoTRegister> {
@Override
public IoTRegister parse(XmlPullParser parser, int initialDepth) throws Exception {
boolean selfOwned = ParserUtils.getBooleanAttribute(parser, "selfOwned", false);
NodeInfo nodeInfo = NodeInfoParser.parse(parser);
List<Tag> tags = new ArrayList<>();
while (parser.getDepth() != initialDepth) {
int event = parser.next();
if (event != XmlPullParser.START_TAG) {
continue;
}
final String element = parser.getName();
Tag.Type type = null;
switch (element) {
case "str":
type = Tag.Type.str;
break;
case "num":
type = Tag.Type.num;
break;
}
if (type == null) {
continue;
}
String name = parser.getAttributeValue(null, "name");
String value = parser.getAttributeValue(null, "value");
tags.add(new Tag(name, type, value));
}
return new IoTRegister(tags, nodeInfo, selfOwned);
}
}

View File

@ -0,0 +1,42 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.provider;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.iot.discovery.element.IoTRemove;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jivesoftware.smackx.iot.parser.NodeInfoParser;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.xmlpull.v1.XmlPullParser;
public class IoTRemoveProvider extends IQProvider<IoTRemove> {
@Override
public IoTRemove parse(XmlPullParser parser, int initialDepth) throws Exception {
Jid jid = ParserUtils.getJidAttribute(parser);
if (jid.hasResource()) {
throw new SmackException("JID must be without resourcepart");
}
BareJid bareJid = jid.asBareJid();
NodeInfo nodeInfo = NodeInfoParser.parse(parser);
return new IoTRemove(bareJid, nodeInfo);
}
}

View File

@ -0,0 +1,33 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.provider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smackx.iot.discovery.element.IoTRemoved;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jivesoftware.smackx.iot.parser.NodeInfoParser;
import org.xmlpull.v1.XmlPullParser;
public class IoTRemovedProvider extends IQProvider<IoTRemoved> {
@Override
public IoTRemoved parse(XmlPullParser parser, int initialDepth) throws Exception {
NodeInfo nodeInfo = NodeInfoParser.parse(parser);
return new IoTRemoved(nodeInfo);
}
}

View File

@ -0,0 +1,33 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.discovery.provider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smackx.iot.discovery.element.IoTUnregister;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.jivesoftware.smackx.iot.parser.NodeInfoParser;
import org.xmlpull.v1.XmlPullParser;
public class IoTUnregisterProvider extends IQProvider<IoTUnregister> {
@Override
public IoTUnregister parse(XmlPullParser parser, int initialDepth) throws Exception {
NodeInfo nodeInfo = NodeInfoParser.parse(parser);
return new IoTUnregister(nodeInfo);
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.discovery.provider;

View File

@ -0,0 +1,105 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.element;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
public final class NodeInfo {
public static final NodeInfo EMPTY = new NodeInfo();
private final String nodeId;
private final String sourceId;
private final String cacheType;
/**
* The internal constructor for the {@link EMPTY} node info marker class.
*/
private NodeInfo() {
this.nodeId = null;
this.sourceId = null;
this.cacheType = null;
}
public NodeInfo(String nodeId, String sourceId, String cacheType) {
this.nodeId = StringUtils.requireNotNullOrEmpty(nodeId, "Node ID must not be null or empty");
this.sourceId = sourceId;
this.cacheType = cacheType;
}
public String getNodeId() {
return nodeId;
}
public String getSourceId() {
return sourceId;
}
public String getCacheType() {
return cacheType;
}
public void appendTo(XmlStringBuilder xml) {
if (nodeId == null) {
return;
}
xml.attribute("nodeId", nodeId).optAttribute("sourceId", sourceId).optAttribute("cacheType", cacheType);
}
@Override
public int hashCode() {
if (this == EMPTY) {
return 0;
}
final int prime = 31;
int result = 1;
result = prime * result + nodeId.hashCode();
result = prime * result + ((sourceId == null) ? 0 : sourceId.hashCode());
result = prime * result + ((cacheType == null) ? 0 : cacheType.hashCode());
return result;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null) {
return false;
}
if (!(other instanceof NodeInfo)) {
return false;
}
NodeInfo otherNodeInfo = (NodeInfo) other;
if (!nodeId.equals(otherNodeInfo.nodeId)) {
return false;
}
if (StringUtils.nullSafeCharSequenceEquals(sourceId, otherNodeInfo.sourceId)
&& StringUtils.nullSafeCharSequenceEquals(cacheType, otherNodeInfo.cacheType)) {
return true;
}
return false;
}
// public static void eventuallyAppend(NodeInfo nodeInfo, XmlStringBuilder xml) {
// if (nodeInfo == null)
// return;
//
// nodeInfo.appendTo(xml);
// }
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.element;

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot;

View File

@ -0,0 +1,35 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.parser;
import org.jivesoftware.smackx.iot.element.NodeInfo;
import org.xmlpull.v1.XmlPullParser;
import static org.jivesoftware.smack.util.StringUtils.isNullOrEmpty;
public class NodeInfoParser {
public static NodeInfo parse(XmlPullParser parser) {
String nodeId = parser.getAttributeValue(null, "nodeId");
String sourceId = parser.getAttributeValue(null, "sourceId");
String cacheType = parser.getAttributeValue(null, "cacheType");
if (isNullOrEmpty(nodeId) && isNullOrEmpty(sourceId) && isNullOrEmpty(cacheType)) {
return NodeInfo.EMPTY;
}
return new NodeInfo(nodeId, sourceId, cacheType);
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.parser;

View File

@ -0,0 +1,319 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.provisioning;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.StanzaTypeFilter;
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.IQ.Type;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.RosterEntry;
import org.jivesoftware.smack.roster.SubscribeListener;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
import org.jivesoftware.smackx.iot.discovery.IoTDiscoveryManager;
import org.jivesoftware.smackx.iot.provisioning.element.ClearCache;
import org.jivesoftware.smackx.iot.provisioning.element.ClearCacheResponse;
import org.jivesoftware.smackx.iot.provisioning.element.Constants;
import org.jivesoftware.smackx.iot.provisioning.element.IoTIsFriend;
import org.jivesoftware.smackx.iot.provisioning.element.IoTIsFriendResponse;
import org.jivesoftware.smackx.iot.provisioning.element.Unfriend;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.util.cache.LruCache;
/**
* A manager for XEP-0324: Internet of Things - Provisioning.
*
* @author Florian Schmaus {@literal <flo@geekplace.eu>}
* @see <a href="http://xmpp.org/extensions/xep-0324.html">XEP-0324: Internet of Things - Provisioning</a>
*/
public final class IoTProvisioningManager extends Manager {
private static final Logger LOGGER = Logger.getLogger(IoTProvisioningManager.class.getName());
private static final StanzaFilter UNFRIEND_MESSAGE = new AndFilter(StanzaTypeFilter.MESSAGE,
new StanzaExtensionFilter(Unfriend.ELEMENT, Unfriend.NAMESPACE));
private static final Map<XMPPConnection, IoTProvisioningManager> INSTANCES = new WeakHashMap<>();
// Ensure a IoTProvisioningManager exists for every connection.
static {
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
public void connectionCreated(XMPPConnection connection) {
getInstanceFor(connection);
}
});
}
/**
* Get the manger instance responsible for the given connection.
*
* @param connection the XMPP connection.
* @return a manager instance.
*/
public static synchronized IoTProvisioningManager getInstanceFor(XMPPConnection connection) {
IoTProvisioningManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new IoTProvisioningManager(connection);
INSTANCES.put(connection, manager);
}
return manager;
}
private final Roster roster;
private final LruCache<Jid, LruCache<BareJid, Void>> negativeFriendshipRequestCache = new LruCache<>(8);
private Jid configuredProvisioningServer;
private IoTProvisioningManager(XMPPConnection connection) {
super(connection);
// Stanza listener for XEP-0324 § 3.2.3.
connection.addAsyncStanzaListener(new StanzaListener() {
@Override
public void processPacket(Stanza stanza) throws NotConnectedException, InterruptedException {
if (!isFromProvisioningService(stanza)) {
return;
}
Message message = (Message) stanza;
Unfriend unfriend = Unfriend.from(message);
BareJid unfriendJid = unfriend.getJid();
final XMPPConnection connection = connection();
Roster roster = Roster.getInstanceFor(connection);
if (!roster.isSubscribedToMyPresence(unfriendJid)) {
LOGGER.warning("Ignoring <unfriend/> request '" + stanza + "' because " + unfriendJid
+ " is already not subscribed to our presence.");
return;
}
Presence unsubscribed = new Presence(Presence.Type.unsubscribed);
unsubscribed.setTo(unfriendJid);
connection.sendStanza(unsubscribed);
}
}, UNFRIEND_MESSAGE);
connection.registerIQRequestHandler(
new AbstractIqRequestHandler(ClearCache.ELEMENT, ClearCache.NAMESPACE, Type.set, Mode.async) {
@Override
public IQ handleIQRequest(IQ iqRequest) {
if (!isFromProvisioningService(iqRequest)) {
return null;
}
ClearCache clearCache = (ClearCache) iqRequest;
// Handle <clearCache/> request.
Jid from = iqRequest.getFrom();
LruCache<BareJid, Void> cache = negativeFriendshipRequestCache.get(from);
if (cache != null) {
cache.clear();
}
return new ClearCacheResponse(clearCache);
}
});
roster = Roster.getInstanceFor(connection);
roster.setSubscribeListener(new SubscribeListener() {
@Override
public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) {
// First check if the subscription request comes from a known registry and accept the request if so.
try {
if (IoTDiscoveryManager.getInstanceFor(connection()).isRegistry(from.asBareJid())) {
return SubscribeAnswer.Approve;
}
}
catch (NoResponseException | XMPPErrorException | NotConnectedException | InterruptedException e) {
LOGGER.log(Level.WARNING, "Could not determine if " + from + " is a registry", e);
}
Jid provisioningServer = null;
try {
provisioningServer = getConfiguredProvisioningServer();
}
catch (NoResponseException | XMPPErrorException | NotConnectedException | InterruptedException e) {
LOGGER.log(Level.WARNING,
"Could not determine privisioning server. Ignoring friend request from " + from, e);
}
if (provisioningServer == null) {
return null;
}
boolean isFriend;
try {
isFriend = isFriend(provisioningServer, from.asBareJid());
}
catch (NoResponseException | XMPPErrorException | NotConnectedException | InterruptedException e) {
LOGGER.log(Level.WARNING, "Could not determine if " + from + " is a friend.", e);
return null;
}
if (isFriend) {
return SubscribeAnswer.Approve;
}
else {
return SubscribeAnswer.Deny;
}
}
});
}
/**
* Set the configured provisioning server. Use <code>null</code> as provisioningServer to use
* automatic discovery of the provisioning server (the default behavior).
*
* @param provisioningServer
*/
public void setConfiguredProvisioningServer(Jid provisioningServer) {
this.configuredProvisioningServer = provisioningServer;
}
public Jid getConfiguredProvisioningServer()
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
if (configuredProvisioningServer == null) {
configuredProvisioningServer = findProvisioningServerComponent();
}
return configuredProvisioningServer;
}
/**
* Try to find a provisioning server component.
*
* @return the XMPP address of the provisioning server component if one was found.
* @throws NoResponseException
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
* @see <a href="http://xmpp.org/extensions/xep-0324.html#servercomponent">XEP-0324 § 3.1.2 Provisioning Server as a server component</a>
*/
public DomainBareJid findProvisioningServerComponent() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
final XMPPConnection connection = connection();
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
List<DiscoverInfo> discoverInfos = sdm.findServicesDiscoverInfo(Constants.IOT_PROVISIONING_NAMESPACE, true, true);
if (discoverInfos.isEmpty()) {
return null;
}
Jid jid = discoverInfos.get(0).getFrom();
assert (jid.isDomainBareJid());
return jid.asDomainBareJid();
}
/**
* As the given provisioning server is the given JID is a friend.
*
* @param provisioningServer the provisioning server to ask.
* @param friendInQuestion the JID to ask about.
* @return <code>true</code> if the JID is a friend, <code>false</code> otherwise.
* @throws NoResponseException
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
*/
public boolean isFriend(Jid provisioningServer, BareJid friendInQuestion) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
LruCache<BareJid, Void> cache = negativeFriendshipRequestCache.get(provisioningServer);
if (cache != null && cache.containsKey(friendInQuestion)) {
// We hit a cached negative isFriend response for this provisioning server.
return false;
}
IoTIsFriend iotIsFriend = new IoTIsFriend(friendInQuestion);
iotIsFriend.setTo(provisioningServer);
IoTIsFriendResponse response = connection().createPacketCollectorAndSend(iotIsFriend).nextResultOrThrow();
assert (response.getJid().equals(friendInQuestion));
boolean isFriend = response.getIsFriendResult();
if (!isFriend) {
// Cache the negative is friend response.
if (cache == null) {
cache = new LruCache<>(1024);
negativeFriendshipRequestCache.put(provisioningServer, cache);
}
cache.put(friendInQuestion, null);
}
return isFriend;
}
public void sendFriendshipRequest(BareJid bareJid) throws NotConnectedException, InterruptedException {
Presence presence = new Presence(Presence.Type.subscribe);
presence.setTo(bareJid);
connection().sendStanza(presence);
}
public void sendFriendshipRequestIfRequired(BareJid jid) throws NotConnectedException, InterruptedException {
RosterEntry entry = roster.getEntry(jid);
if (entry != null && entry.canSeeHisPresence()) {
return;
}
sendFriendshipRequest(jid);
}
public boolean isBefriended(Jid friendInQuestion) {
return roster.isSubscribedToMyPresence(friendInQuestion);
}
public void unfriend(Jid friend) throws NotConnectedException, InterruptedException {
if (isBefriended(friend)) {
Presence presence = new Presence(Presence.Type.unsubscribed);
presence.setTo(friend);
connection().sendStanza(presence);
}
}
private boolean isFromProvisioningService(Stanza stanza) {
Jid provisioningServer;
try {
provisioningServer = getConfiguredProvisioningServer();
}
catch (NotConnectedException | InterruptedException | NoResponseException | XMPPErrorException e) {
LOGGER.log(Level.WARNING, "Could determine provisioning server", e);
return false;
}
if (provisioningServer == null) {
LOGGER.warning("Ignoring request '" + stanza
+ "' because no provisioning server configured.");
return false;
}
if (!provisioningServer.equals(stanza.getFrom())) {
LOGGER.warning("Ignoring request '" + stanza + "' because not from provising server '"
+ provisioningServer + "'.");
return false;
}
return true;
}
}

View File

@ -0,0 +1,32 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.provisioning.element;
import org.jivesoftware.smack.packet.SimpleIQ;
public class ClearCache extends SimpleIQ {
public static final String ELEMENT = "clearCache";
public static final String NAMESPACE = Constants.IOT_PROVISIONING_NAMESPACE;
public ClearCache() {
super(ELEMENT, NAMESPACE);
// <clearCache/> IQs are always of type 'get' (XEP-0324 § 3.5.1, see also the XEPs history remarks)
setType(Type.get);
}
}

View File

@ -0,0 +1,37 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.provisioning.element;
import org.jivesoftware.smack.packet.SimpleIQ;
public class ClearCacheResponse extends SimpleIQ {
public static final String ELEMENT = "clearCacheResponse";
public static final String NAMESPACE = Constants.IOT_PROVISIONING_NAMESPACE;
public ClearCacheResponse() {
super(ELEMENT, NAMESPACE);
// <clearCacheResponse/> IQs are always of type 'result' (XEP-0324 § 3.5.1, see also the XEPs history remarks)
setType(Type.result);
}
public ClearCacheResponse(ClearCache clearCacheRequest) {
this();
setStanzaId(clearCacheRequest.getStanzaId());
setTo(clearCacheRequest.getFrom());
}
}

View File

@ -0,0 +1,23 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.provisioning.element;
public class Constants {
public static final String IOT_PROVISIONING_NAMESPACE = "urn:xmpp:iot:provisioning";
}

View File

@ -0,0 +1,41 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.provisioning.element;
import org.jivesoftware.smack.packet.IQ;
import org.jxmpp.jid.Jid;
public class IoTIsFriend extends IQ {
public static final String ELEMENT = "isFriend";
public static final String NAMESPACE = Constants.IOT_PROVISIONING_NAMESPACE;
private final Jid jid;
public IoTIsFriend(Jid jid) {
super(ELEMENT, NAMESPACE);
this.jid = jid;
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.attribute("jid", jid);
xml.setEmptyElement();
return xml;
}
}

View File

@ -0,0 +1,52 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.provisioning.element;
import org.jivesoftware.smack.packet.IQ;
import org.jxmpp.jid.BareJid;
public class IoTIsFriendResponse extends IQ {
public static final String ELEMENT = "isFriend";
public static final String NAMESPACE = Constants.IOT_PROVISIONING_NAMESPACE;
private final BareJid jid;
private final boolean result;
public IoTIsFriendResponse(BareJid jid, boolean result) {
super(ELEMENT, NAMESPACE);
this.jid = jid;
this.result = result;
}
public BareJid getJid() {
return jid;
}
public boolean getIsFriendResult() {
return result;
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.attribute("jid", jid);
xml.attribute("result", result);
return xml;
}
}

View File

@ -0,0 +1,59 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.provisioning.element;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jxmpp.jid.BareJid;
public class Unfriend implements ExtensionElement {
public static final String ELEMENT = "UNFRIEND";
public static final String NAMESPACE = Constants.IOT_PROVISIONING_NAMESPACE;
private final BareJid jid;
public Unfriend(BareJid jid) {
this.jid = jid;
}
public BareJid getJid() {
return jid;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this);
xml.attribute("jid", jid);
xml.closeEmptyElement();
return xml;
}
public static Unfriend from(Message message) {
return message.getExtension(ELEMENT, NAMESPACE);
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.provisioning.element;

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.provisioning;

View File

@ -0,0 +1,31 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.provisioning.provider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smackx.iot.provisioning.element.ClearCache;
import org.xmlpull.v1.XmlPullParser;
public class ClearCacheProvider extends IQProvider<ClearCache> {
@Override
public ClearCache parse(XmlPullParser parser, int initialDepth) throws Exception {
return new ClearCache();
}
}

View File

@ -0,0 +1,31 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.provisioning.provider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smackx.iot.provisioning.element.ClearCacheResponse;
import org.xmlpull.v1.XmlPullParser;
public class ClearCacheResponseProvider extends IQProvider<ClearCacheResponse> {
@Override
public ClearCacheResponse parse(XmlPullParser parser, int initialDepth) throws Exception {
return new ClearCacheResponse();
}
}

View File

@ -0,0 +1,34 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.provisioning.provider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.iot.provisioning.element.IoTIsFriend;
import org.jxmpp.jid.Jid;
import org.xmlpull.v1.XmlPullParser;
public class IoTIsFriendProvider extends IQProvider<IoTIsFriend> {
@Override
public IoTIsFriend parse(XmlPullParser parser, int initialDepth) throws Exception {
Jid jid = ParserUtils.getJidAttribute(parser);
return new IoTIsFriend(jid);
}
}

View File

@ -0,0 +1,37 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.iot.provisioning.provider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.iot.provisioning.element.IoTIsFriendResponse;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
import org.xmlpull.v1.XmlPullParser;
public class IoTIsFriendResponseProvider extends IQProvider<IoTIsFriendResponse> {
@Override
public IoTIsFriendResponse parse(XmlPullParser parser, int initialDepth) throws Exception {
Jid jid = ParserUtils.getJidAttribute(parser);
BareJid bareJid = jid.asBareJid();
boolean result = ParserUtils.getBooleanAttribute(parser, "result");
return new IoTIsFriendResponse(bareJid, result);
}
}

View File

@ -0,0 +1,33 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.iot.provisioning.provider;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.iot.provisioning.element.Unfriend;
import org.jxmpp.jid.BareJid;
import org.xmlpull.v1.XmlPullParser;
public class UnfriendProvider extends ExtensionElementProvider<Unfriend> {
@Override
public Unfriend parse(XmlPullParser parser, int initialDepth) throws Exception {
BareJid jid = ParserUtils.getBareJidAttribute(parser);
return new Unfriend(jid);
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright © 2016 Florian Schmaus
*
* 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.
*/
/**
* Smack's API for XMPP IoT.
*/
package org.jivesoftware.smackx.iot.provisioning.provider;

View File

@ -52,4 +52,97 @@
<className>org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider</className> <className>org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider</className>
</extensionProvider> </extensionProvider>
<!-- XEP-0347: Internet of Things - Discovery -->
<iqProvider>
<elementName>register</elementName>
<namespace>urn:xmpp:iot:discovery</namespace>
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTRegisterProvider</className>
</iqProvider>
<iqProvider>
<elementName>claimed</elementName>
<namespace>urn:xmpp:iot:discovery</namespace>
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTClaimedProvider</className>
</iqProvider>
<iqProvider>
<elementName>disown</elementName>
<namespace>urn:xmpp:iot:discovery</namespace>
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTDisownProvider</className>
</iqProvider>
<iqProvider>
<elementName>disowned</elementName>
<namespace>urn:xmpp:iot:discovery</namespace>
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTDisownedProvider</className>
</iqProvider>
<iqProvider>
<elementName>remove</elementName>
<namespace>urn:xmpp:iot:discovery</namespace>
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTRemoveProvider</className>
</iqProvider>
<iqProvider>
<elementName>disown</elementName>
<namespace>urn:xmpp:iot:discovery</namespace>
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTRemovedProvider</className>
</iqProvider>
<iqProvider>
<elementName>unregister</elementName>
<namespace>urn:xmpp:iot:discovery</namespace>
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTUnregisterProvider</className>
</iqProvider>
<!-- XEP-0324: Internet of Things - Provisioning -->
<iqProvider>
<elementName>isFriend</elementName>
<namespace>urn:xmpp:iot:provisioning</namespace>
<className>org.jivesoftware.smackx.iot.provisioning.provider.IoTIsFriendProvider</className>
</iqProvider>
<iqProvider>
<elementName>isFriendResponse</elementName>
<namespace>urn:xmpp:iot:provisioning</namespace>
<className>org.jivesoftware.smackx.iot.provisioning.provider.IoTIsFriendResponseProvider</className>
</iqProvider>
<iqProvider>
<elementName>clearCache</elementName>
<namespace>urn:xmpp:iot:provisioning</namespace>
<className>org.jivesoftware.smackx.iot.provisioning.provider.ClearCacheProvider</className>
</iqProvider>
<iqProvider>
<elementName>clearCacheResponse</elementName>
<namespace>urn:xmpp:iot:provisioning</namespace>
<className>org.jivesoftware.smackx.iot.provisioning.provider.ClearCacheResponseProvider</className>
</iqProvider>
<extensionProvider>
<elementName>unfriend</elementName>
<namespace>urn:xmpp:iot:provisioning</namespace>
<className>org.jivesoftware.smackx.iot.provisioning.provider.UnfriendProvider</className>
</extensionProvider>
<!-- XEP-0323: Internet of Things - Data -->
<iqProvider>
<elementName>req</elementName>
<namespace>urn:xmpp:iot:data</namespace>
<className>org.jivesoftware.smackx.iot.data.provider.IoTDataRequestProvider</className>
</iqProvider>
<iqProvider>
<elementName>accepted</elementName>
<namespace>urn:xmpp:iot:data</namespace>
<className>org.jivesoftware.smackx.iot.data.provider.IoTDataReadOutAcceptedProvider</className>
</iqProvider>
<extensionProvider>
<elementName>fields</elementName>
<namespace>urn:xmpp:iot:data</namespace>
<className>org.jivesoftware.smackx.iot.data.provider.IoTFieldsExtensionProvider</className>
</extensionProvider>
<!-- XEP-0325: Internet of Things - Control -->
<iqProvider>
<elementName>set</elementName>
<namespace>urn:xmpp:iot:control</namespace>
<className>org.jivesoftware.smackx.iot.control.provider.IoTSetRequestProvider</className>
</iqProvider>
<iqProvider>
<elementName>setResponse</elementName>
<namespace>urn:xmpp:iot:control</namespace>
<className>org.jivesoftware.smackx.iot.control.provider.IoTSetResponseProvider</className>
</iqProvider>
</smackProviders> </smackProviders>

View File

@ -1,5 +1,8 @@
<smack> <smack>
<startupClasses> <startupClasses>
<className>org.jivesoftware.smackx.hoxt.HOXTManager</className> <className>org.jivesoftware.smackx.hoxt.HOXTManager</className>
<className>org.jivesoftware.smackx.iot.data.IoTDataManager</className>
<className>org.jivesoftware.smackx.iot.discovery.IoTDiscoveryManager</className>
<className>org.jivesoftware.smackx.iot.provisioning.IoTProvisioningManager</className>
</startupClasses> </startupClasses>
</smack> </smack>

View File

@ -1074,13 +1074,7 @@ public final class Roster extends Manager {
if (entry == null) { if (entry == null) {
return false; return false;
} }
switch (entry.getType()) { return entry.canSeeMyPresence();
case from:
case both:
return true;
default:
return false;
}
} }
/** /**

View File

@ -176,6 +176,39 @@ public final class RosterEntry extends Manager {
return item.isSubscriptionPending(); return item.isSubscriptionPending();
} }
/**
* Check if the contact is subscribed to "my" presence. This allows the contact to see the presence information.
*
* @return true if the contact has a presence subscription.
* @since 4.2
*/
public boolean canSeeMyPresence() {
switch (getType()) {
case from:
case both:
return true;
default:
return false;
}
}
/**
* Check if we are subscribed to the contact's presence. If <code>true</code> then the contact has allowed us to
* receive presence information.
*
* @return true if we are subscribed to the contact's presence.
* @since 4.2
*/
public boolean canSeeHisPresence() {
switch (getType()) {
case to:
case both:
return true;
default:
return false;
}
}
public String toString() { public String toString() {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
if (getName() != null) { if (getName() != null) {

View File

@ -0,0 +1,83 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* 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.roster;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
public class RosterUtil {
public static void waitUntilOtherEntityIsSubscribed(Roster roster, BareJid otherEntity, long timeoutMillis)
throws InterruptedException, TimeoutException {
Date deadline = new Date(System.currentTimeMillis() + timeoutMillis);
waitUntilOtherEntityIsSubscribed(roster, otherEntity, deadline);
}
public static void waitUntilOtherEntityIsSubscribed(Roster roster, final BareJid otherEntity, Date deadline)
throws InterruptedException, TimeoutException {
final Lock lock = new ReentrantLock();
final Condition maybeSubscribed = lock.newCondition();
RosterListener rosterListener = new AbstractRosterListener() {
private void signal() {
lock.lock();
try {
// No need to use signalAll() here.
maybeSubscribed.signal();
}
finally {
lock.unlock();
}
}
@Override
public void entriesAdded(Collection<Jid> addresses) {
signal();
}
@Override
public void entriesUpdated(Collection<Jid> addresses) {
signal();
}
};
roster.addRosterListener(rosterListener);
boolean stillWaiting = true;
// Using the example code pattern from Condition.awaitUntil(Date) javadoc.
lock.lock();
try {
while (!roster.isSubscribedToMyPresence(otherEntity)) {
if (!stillWaiting) {
throw new TimeoutException();
}
stillWaiting = maybeSubscribed.awaitUntil(deadline);
}
}
finally {
lock.unlock();
// Make sure the listener is removed, so we don't leak it.
roster.removeRosterListener(rosterListener);
}
}
}

View File

@ -10,6 +10,7 @@ dependencies {
compile project(':smack-java7') compile project(':smack-java7')
compile project(':smack-tcp') compile project(':smack-tcp')
compile project(':smack-extensions') compile project(':smack-extensions')
compile project(':smack-experimental')
compile 'org.reflections:reflections:0.9.9-RC1' compile 'org.reflections:reflections:0.9.9-RC1'
compile 'eu.geekplace.javapinning:java-pinning-java7:1.1.0-alpha1' compile 'eu.geekplace.javapinning:java-pinning-java7:1.1.0-alpha1'
compile "junit:junit:$junitVersion" compile "junit:junit:$junitVersion"

View File

@ -16,10 +16,13 @@
*/ */
package org.igniterealtime.smack.inttest; package org.igniterealtime.smack.inttest;
import java.util.Random;
import java.util.logging.Logger; import java.util.logging.Logger;
public abstract class AbstractSmackIntTest { public abstract class AbstractSmackIntTest {
protected static final Logger LOGGER = Logger.getLogger(AbstractSmackIntTest.class.getName()); protected static final Logger LOGGER = Logger.getLogger(AbstractSmackIntTest.class.getName());
protected static final Random INSECURE_RANDOM = new Random();
} }

View File

@ -30,6 +30,11 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest
*/ */
protected final XMPPConnection conTwo; protected final XMPPConnection conTwo;
/**
* The third connection.
*/
protected final XMPPConnection conThree;
/** /**
* An alias for the first connection {@link #conOne}. * An alias for the first connection {@link #conOne}.
*/ */
@ -42,6 +47,7 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest
public AbstractSmackIntegrationTest(SmackIntegrationTestEnvironment environment) { public AbstractSmackIntegrationTest(SmackIntegrationTestEnvironment environment) {
this.connection = this.conOne = environment.conOne; this.connection = this.conOne = environment.conOne;
this.conTwo = environment.conTwo; this.conTwo = environment.conTwo;
this.conThree = environment.conThree;
if (environment.configuration.replyTimeout > 0) { if (environment.configuration.replyTimeout > 0) {
this.defaultTimeout = environment.configuration.replyTimeout; this.defaultTimeout = environment.configuration.replyTimeout;
} else { } else {

View File

@ -53,6 +53,10 @@ public final class Configuration {
public final String accountTwoPassword; public final String accountTwoPassword;
public final String accountThreeUsername;
public final String accountThreePassword;
public final boolean debug; public final boolean debug;
public final Set<String> enabledTests; public final Set<String> enabledTests;
@ -63,7 +67,7 @@ public final class Configuration {
private Configuration(DomainBareJid service, String serviceTlsPin, SecurityMode securityMode, int replyTimeout, private Configuration(DomainBareJid service, String serviceTlsPin, SecurityMode securityMode, int replyTimeout,
boolean debug, String accountOneUsername, String accountOnePassword, String accountTwoUsername, boolean debug, String accountOneUsername, String accountOnePassword, String accountTwoUsername,
String accountTwoPassword, Set<String> enabledTests, Set<String> disabledTests, String accountTwoPassword, String accountThreeUsername, String accountThreePassword, Set<String> enabledTests, Set<String> disabledTests,
Set<String> testPackages) { Set<String> testPackages) {
this.service = Objects.requireNonNull(service, this.service = Objects.requireNonNull(service,
"'service' must be set. Either via 'properties' files or via system property 'sinttest.service'."); "'service' must be set. Either via 'properties' files or via system property 'sinttest.service'.");
@ -83,6 +87,8 @@ public final class Configuration {
this.accountOnePassword = accountOnePassword; this.accountOnePassword = accountOnePassword;
this.accountTwoUsername = accountTwoUsername; this.accountTwoUsername = accountTwoUsername;
this.accountTwoPassword = accountTwoPassword; this.accountTwoPassword = accountTwoPassword;
this.accountThreeUsername = accountThreeUsername;
this.accountThreePassword = accountThreePassword;
this.enabledTests = enabledTests; this.enabledTests = enabledTests;
this.disabledTests = disabledTests; this.disabledTests = disabledTests;
this.testPackages = testPackages; this.testPackages = testPackages;
@ -110,6 +116,10 @@ public final class Configuration {
private String accountTwoPassword; private String accountTwoPassword;
public String accountThreeUsername;
public String accountThreePassword;
private boolean debug; private boolean debug;
private Set<String> enabledTests; private Set<String> enabledTests;
@ -153,11 +163,13 @@ public final class Configuration {
} }
public Builder setUsernamesAndPassword(String accountOneUsername, String accountOnePassword, public Builder setUsernamesAndPassword(String accountOneUsername, String accountOnePassword,
String accountTwoUsername, String accountTwoPassword) { String accountTwoUsername, String accountTwoPassword, String accountThreeUsername, String accountThreePassword) {
this.accountOneUsername = accountOneUsername; this.accountOneUsername = StringUtils.requireNotNullOrEmpty(accountOneUsername, "accountOneUsername must not be null or empty");
this.accountOnePassword = accountOnePassword; this.accountOnePassword = StringUtils.requireNotNullOrEmpty(accountOnePassword, "accountOnePassword must not be null or empty");
this.accountTwoUsername = accountTwoUsername; this.accountTwoUsername = StringUtils.requireNotNullOrEmpty(accountTwoUsername, "accountTwoUsername must not be null or empty");
this.accountTwoPassword = accountTwoPassword; this.accountTwoPassword = StringUtils.requireNotNullOrEmpty(accountTwoPassword, "accountTwoPasswordmust not be null or empty");
this.accountThreeUsername = StringUtils.requireNotNullOrEmpty(accountThreeUsername, "accountThreeUsername must not be null or empty");
this.accountThreePassword = StringUtils.requireNotNullOrEmpty(accountThreePassword, "accountThreePassword must not be null or empty");
return this; return this;
} }
@ -213,7 +225,7 @@ public final class Configuration {
public Configuration build() { public Configuration build() {
return new Configuration(service, serviceTlsPin, securityMode, replyTimeout, debug, accountOneUsername, return new Configuration(service, serviceTlsPin, securityMode, replyTimeout, debug, accountOneUsername,
accountOnePassword, accountTwoUsername, accountTwoPassword, enabledTests, disabledTests, accountOnePassword, accountTwoUsername, accountTwoPassword, accountThreeUsername, accountThreePassword, enabledTests, disabledTests,
testPackages); testPackages);
} }
} }
@ -252,7 +264,9 @@ public final class Configuration {
String accountOnePassword = properties.getProperty("accountOnePassword"); String accountOnePassword = properties.getProperty("accountOnePassword");
String accountTwoUsername = properties.getProperty("accountTwoUsername"); String accountTwoUsername = properties.getProperty("accountTwoUsername");
String accountTwoPassword = properties.getProperty("accountTwoPassword"); String accountTwoPassword = properties.getProperty("accountTwoPassword");
builder.setUsernamesAndPassword(accountOneUsername, accountOnePassword, accountTwoUsername, accountTwoPassword); String accountThreeUsername = properties.getProperty("accountThreeUsername");
String accountThreePassword = properties.getProperty("accountThreePassword");
builder.setUsernamesAndPassword(accountOneUsername, accountOnePassword, accountTwoUsername, accountTwoPassword, accountThreeUsername, accountThreePassword);
builder.setDebug(properties.getProperty("debug")); builder.setDebug(properties.getProperty("debug"));
builder.setEnabledTests(properties.getProperty("enabledTests")); builder.setEnabledTests(properties.getProperty("enabledTests"));
@ -262,7 +276,7 @@ public final class Configuration {
return builder.build(); return builder.build();
} }
private static File findPropertiesFile() throws IOException { private static File findPropertiesFile() {
List<String> possibleLocations = new LinkedList<>(); List<String> possibleLocations = new LinkedList<>();
possibleLocations.add("properties"); possibleLocations.add("properties");
String userHome = System.getProperty("user.home"); String userHome = System.getProperty("user.home");

View File

@ -24,14 +24,17 @@ public class SmackIntegrationTestEnvironment {
public final XMPPTCPConnection conTwo; public final XMPPTCPConnection conTwo;
public final XMPPTCPConnection conThree;
public final String testRunId; public final String testRunId;
public final Configuration configuration; public final Configuration configuration;
SmackIntegrationTestEnvironment(XMPPTCPConnection conOne, XMPPTCPConnection conTwo, String testRunId, SmackIntegrationTestEnvironment(XMPPTCPConnection conOne, XMPPTCPConnection conTwo, XMPPTCPConnection conThree, String testRunId,
Configuration configuration) { Configuration configuration) {
this.conOne = conOne; this.conOne = conOne;
this.conTwo = conTwo; this.conTwo = conTwo;
this.conThree = conThree;
this.testRunId = testRunId; this.testRunId = testRunId;
this.configuration = configuration; this.configuration = configuration;
} }

Some files were not shown because too many files have changed in this diff Show More