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