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