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