001/** 002 * 003 * Copyright 2016 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.igniterealtime.smack.smackrepl; 018 019import org.jivesoftware.smack.XMPPException; 020import org.jivesoftware.smack.packet.Presence; 021import org.jivesoftware.smack.roster.Roster; 022import org.jivesoftware.smack.roster.RosterUtil; 023 024import java.io.IOException; 025import java.util.Collections; 026import java.util.List; 027import java.util.concurrent.TimeoutException; 028 029import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; 030import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; 031import org.jivesoftware.smack.SmackException; 032import org.jivesoftware.smack.tcp.XMPPTCPConnection; 033import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; 034import org.jivesoftware.smack.util.StringUtils; 035import org.jivesoftware.smackx.iot.IoTDiscoveryIntegrationTest; 036import org.jivesoftware.smackx.iot.Thing; 037import org.jivesoftware.smackx.iot.data.IoTDataManager; 038import org.jivesoftware.smackx.iot.data.ThingMomentaryReadOutRequest; 039import org.jivesoftware.smackx.iot.data.ThingMomentaryReadOutResult; 040import org.jivesoftware.smackx.iot.data.element.IoTDataField; 041import org.jivesoftware.smackx.iot.data.element.IoTDataField.IntField; 042import org.jivesoftware.smackx.iot.data.element.IoTFieldsExtension; 043import org.jivesoftware.smackx.iot.discovery.AbstractThingStateChangeListener; 044import org.jivesoftware.smackx.iot.discovery.IoTDiscoveryManager; 045import org.jivesoftware.smackx.iot.discovery.ThingState; 046import org.jivesoftware.smackx.iot.provisioning.BecameFriendListener; 047import org.jivesoftware.smackx.iot.provisioning.IoTProvisioningManager; 048import org.jxmpp.jid.BareJid; 049import org.jxmpp.jid.EntityBareJid; 050import org.jxmpp.jid.impl.JidCreate; 051 052public class IoT { 053 054 // A 10 minute timeout. 055 private static final long TIMEOUT = 10 * 60 * 1000; 056 057 private interface IotScenario { 058 void iotScenario(XMPPTCPConnection dataThingConnection, XMPPTCPConnection readinThingConnection) throws XMPPException, SmackException, IOException, InterruptedException, TimeoutException, Exception; 059 } 060 061 public static void iotScenario(String dataThingJidString, String dataThingPassword, String readingThingJidString, 062 String readingThingPassword, IotScenario scenario) throws TimeoutException, Exception { 063 final EntityBareJid dataThingJid = JidCreate.entityBareFrom(dataThingJidString); 064 final EntityBareJid readingThingJid = JidCreate.entityBareFrom(readingThingJidString); 065 066 final XMPPTCPConnectionConfiguration dataThingConnectionConfiguration = XMPPTCPConnectionConfiguration.builder() 067 .setUsernameAndPassword(dataThingJid.getLocalpart(), dataThingPassword) 068 .setXmppDomain(dataThingJid.asDomainBareJid()).setSecurityMode(SecurityMode.disabled) 069 .setDebuggerEnabled(true).build(); 070 final XMPPTCPConnectionConfiguration readingThingConnectionConfiguration = XMPPTCPConnectionConfiguration 071 .builder().setUsernameAndPassword(readingThingJid.getLocalpart(), readingThingPassword) 072 .setXmppDomain(readingThingJid.asDomainBareJid()).setSecurityMode(SecurityMode.disabled) 073 .setDebuggerEnabled(true).build(); 074 075 final XMPPTCPConnection dataThingConnection = new XMPPTCPConnection(dataThingConnectionConfiguration); 076 final XMPPTCPConnection readingThingConnection = new XMPPTCPConnection(readingThingConnectionConfiguration); 077 078 dataThingConnection.setReplyTimeout(TIMEOUT); 079 readingThingConnection.setReplyTimeout(TIMEOUT); 080 081 dataThingConnection.setUseStreamManagement(false); 082 readingThingConnection.setUseStreamManagement(false); 083 084 try { 085 dataThingConnection.connect().login(); 086 readingThingConnection.connect().login(); 087 scenario.iotScenario(dataThingConnection, readingThingConnection); 088 } finally { 089 dataThingConnection.disconnect(); 090 readingThingConnection.disconnect(); 091 } 092 } 093 094 public static void iotReadOutScenario(String dataThingJidString, String dataThingPassword, String readingThingJidString, 095 String readingThingPassword) 096 throws Exception { 097 iotScenario(dataThingJidString, dataThingPassword, readingThingJidString, readingThingPassword, READ_OUT_SCENARIO); 098 } 099 100 public static final IotScenario READ_OUT_SCENARIO = new IotScenario() { 101 @Override 102 public void iotScenario(XMPPTCPConnection dataThingConnection, XMPPTCPConnection readingThingConnection) throws TimeoutException, Exception { 103 ThingState dataThingState = actAsDataThing(dataThingConnection); 104 105 final SimpleResultSyncPoint syncPoint = new SimpleResultSyncPoint(); 106 dataThingState.setThingStateChangeListener(new AbstractThingStateChangeListener() { 107 @Override 108 public void owned(BareJid jid) { 109 syncPoint.signal(); 110 } 111 }); 112 // Wait until the thing is owned. 113 syncPoint.waitForResult(TIMEOUT); 114 printStatus("OWNED - Thing now onwed by " + dataThingState.getOwner()); 115 116 // Make sure things are befriended. 117 IoTProvisioningManager readingThingProvisioningManager = IoTProvisioningManager.getInstanceFor(readingThingConnection); 118 readingThingProvisioningManager.sendFriendshipRequestIfRequired(dataThingConnection.getUser().asBareJid()); 119 120 Roster dataThingRoster = Roster.getInstanceFor(dataThingConnection); 121 RosterUtil.waitUntilOtherEntityIsSubscribed(dataThingRoster, readingThingConnection.getUser().asBareJid(), TIMEOUT); 122 printStatus("FRIENDSHIP ACCEPTED - Trying to read out data"); 123 124 IoTDataManager readingThingDataManager = IoTDataManager.getInstanceFor(readingThingConnection); 125 List<IoTFieldsExtension> values = readingThingDataManager.requestMomentaryValuesReadOut(dataThingConnection.getUser()); 126 if (values.size() != 1) { 127 throw new IllegalStateException("Unexpected number of values returned: " + values.size()); 128 } 129 IoTFieldsExtension field = values.get(0); 130 printStatus("DATA READ-OUT SUCCESS: " + field.toXML()); 131 printStatus("IoT SCENARIO FINISHED SUCCESSFULLY"); 132 } 133 }; 134 135 public static void iotOwnerApprovesFriendScenario(String dataThingJidString, String dataThingPassword, 136 String readingThingJidString, String readingThingPassword) throws Exception { 137 iotScenario(dataThingJidString, dataThingPassword, readingThingJidString, readingThingPassword, 138 OWNER_APPROVES_FRIEND_SCENARIO); 139 } 140 141 public static final IotScenario OWNER_APPROVES_FRIEND_SCENARIO = new IotScenario() { 142 @Override 143 public void iotScenario(XMPPTCPConnection dataThingConnection, XMPPTCPConnection readingThingConnection) throws TimeoutException, Exception { 144 // First ensure that the two XMPP entities are not already subscribed to each other presences. 145 RosterUtil.ensureNotSubscribedToEachOther(dataThingConnection, readingThingConnection); 146 147 final BareJid dataThingBareJid = dataThingConnection.getUser().asBareJid(); 148 final BareJid readingThingBareJid = readingThingConnection.getUser().asBareJid(); 149 final ThingState dataThingState = actAsDataThing(dataThingConnection); 150 151 printStatus("WAITING for 'claimed' notification. Please claim thing now"); 152 final SimpleResultSyncPoint syncPoint = new SimpleResultSyncPoint(); 153 dataThingState.setThingStateChangeListener(new AbstractThingStateChangeListener() { 154 @Override 155 public void owned(BareJid jid) { 156 syncPoint.signal(); 157 } 158 }); 159 // Wait until the thing is owned. 160 syncPoint.waitForResult(TIMEOUT); 161 printStatus("OWNED - Thing now onwed by " + dataThingState.getOwner()); 162 163 // Now, ReadingThing sends a friendship request to data thing, which 164 // will proxy the request to its provisioning service, which will 165 // likely return that both a not friends since the owner did not 166 // authorize the friendship yet. 167 final SimpleResultSyncPoint friendshipApprovedSyncPoint = new SimpleResultSyncPoint(); 168 final IoTProvisioningManager readingThingProvisioningManager = IoTProvisioningManager.getInstanceFor(readingThingConnection); 169 final BecameFriendListener becameFriendListener = new BecameFriendListener() { 170 @Override 171 public void becameFriend(BareJid jid, Presence presence) { 172 if (jid.equals(dataThingBareJid)) { 173 friendshipApprovedSyncPoint.signal(); 174 } 175 } 176 }; 177 readingThingProvisioningManager.addBecameFriendListener(becameFriendListener); 178 179 try { 180 readingThingProvisioningManager 181 .sendFriendshipRequestIfRequired(dataThingConnection.getUser().asBareJid()); 182 friendshipApprovedSyncPoint.waitForResult(TIMEOUT); 183 } finally { 184 readingThingProvisioningManager.removeBecameFriendListener(becameFriendListener); 185 } 186 187 printStatus("FRIENDSHIP APPROVED - ReadingThing " + readingThingBareJid + " is now a friend of DataThing " + dataThingBareJid); 188 } 189 }; 190 191 private static ThingState actAsDataThing(XMPPTCPConnection connection) throws XMPPException, SmackException, InterruptedException { 192 final String key = StringUtils.randomString(12); 193 final String sn = StringUtils.randomString(12); 194 Thing dataThing = Thing.builder() 195 .setKey(key) 196 .setSerialNumber(sn) 197 .setManufacturer("IgniteRealtime") 198 .setModel("Smack") 199 .setVersion("0.1") 200 .setMomentaryReadOutRequestHandler(new ThingMomentaryReadOutRequest() { 201 @Override 202 public void momentaryReadOutRequest(ThingMomentaryReadOutResult callback) { 203 IoTDataField.IntField field = new IntField("timestamp", (int) (System.currentTimeMillis() / 1000)); 204 callback.momentaryReadOut(Collections.singletonList(field)); 205 } 206 }) 207 .build(); 208 IoTDiscoveryManager iotDiscoveryManager = IoTDiscoveryManager.getInstanceFor(connection); 209 ThingState state = IoTDiscoveryIntegrationTest.registerThing(iotDiscoveryManager, dataThing); 210 printStatus("SUCCESS: Thing registered:" + dataThing); 211 return state; 212 } 213 214 private static void printStatus(CharSequence status) { 215 // CHECKSTYLE:OFF 216 System.out.println(status); 217 // CHECKSTYLE:ON 218 } 219 220 public static void main(String[] args) throws TimeoutException, Exception { 221 if (args.length != 4) { 222 throw new IllegalArgumentException(); 223 } 224 iotOwnerApprovesFriendScenario(args[0], args[1], args[2], args[3]); 225 } 226 227}