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}