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 |
| accountTwoUsername | Username 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 |
| enabledTests | List of enabled 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
@ -77,8 +77,12 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental
| 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.
| [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. |
| [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 |
| 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
try {
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
// longer login automatically. So change this and the

View File

@ -25,13 +25,7 @@ public class EmptyResultIQ extends IQ {
public EmptyResultIQ(IQ request) {
this();
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());
initialzeAsResultFor(request);
}
@Override

View File

@ -215,6 +215,17 @@ public abstract class IQ extends Stanza {
*/
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}
* 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
* {@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.
* @deprecated use {@link #createErrorResponse(IQ, org.jivesoftware.smack.packet.XMPPError.Builder)} instead.
*/
@Deprecated
public static ErrorIQ createErrorResponse(final IQ request, final XMPPError 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.
*/
public Presence(Type type) {
// Ensure that the stanza ID is set by calling super().
super();
setType(type);
}
@ -85,6 +87,8 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* @param mode the mode type for this presence update.
*/
public Presence(Type type, String status, int priority, Mode mode) {
// Ensure that the stanza ID is set by calling super().
super();
setType(type);
setStatus(status);
setPriority(priority);

View File

@ -23,6 +23,7 @@ import java.text.ParseException;
import java.util.Date;
import java.util.Locale;
import org.jivesoftware.smack.SmackException;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.Jid;
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) {
String valueString = parser.getAttributeValue("", name);
if (valueString == null)

View File

@ -238,6 +238,10 @@ public class XmlStringBuilder implements Appendable, CharSequence {
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,
* 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>
</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>

View File

@ -1,5 +1,8 @@
<smack>
<startupClasses>
<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>
</smack>
</smack>

View File

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

View File

@ -176,6 +176,39 @@ public final class RosterEntry extends Manager {
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() {
StringBuilder buf = new StringBuilder();
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-tcp')
compile project(':smack-extensions')
compile project(':smack-experimental')
compile 'org.reflections:reflections:0.9.9-RC1'
compile 'eu.geekplace.javapinning:java-pinning-java7:1.1.0-alpha1'
compile "junit:junit:$junitVersion"

View File

@ -16,10 +16,13 @@
*/
package org.igniterealtime.smack.inttest;
import java.util.Random;
import java.util.logging.Logger;
public abstract class AbstractSmackIntTest {
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;
/**
* The third connection.
*/
protected final XMPPConnection conThree;
/**
* An alias for the first connection {@link #conOne}.
*/
@ -42,6 +47,7 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest
public AbstractSmackIntegrationTest(SmackIntegrationTestEnvironment environment) {
this.connection = this.conOne = environment.conOne;
this.conTwo = environment.conTwo;
this.conThree = environment.conThree;
if (environment.configuration.replyTimeout > 0) {
this.defaultTimeout = environment.configuration.replyTimeout;
} else {

View File

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

View File

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

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