From e3d3977f5a7f017df4ab9243f61b447cee10d149 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 1 Nov 2014 12:18:30 +0100 Subject: [PATCH 1/8] DRAFT/WIP smack-serverless --- settings.gradle | 3 +- .../smack/ConnectionConfiguration.java | 1 + .../jivesoftware/smack/PacketCollector.java | 4 + .../org/jivesoftware/smack/util/Tuple.java | 27 + .../smackx/caps/EntityCapsManager.java | 56 +- .../smackx/disco/ServiceDiscoveryManager.java | 6 +- smack-serverless/build.gradle | 8 + .../serverless/JmDNSPresenceDiscoverer.java | 133 +++ .../smack/serverless/JmDNSService.java | 255 +++++ .../serverless/LLConnectionConfiguration.java | 123 +++ .../smack/serverless/LLPresence.java | 280 +++++ .../serverless/LLPresenceDiscoverer.java | 109 ++ .../smack/serverless/LLPresenceListener.java | 37 + .../smack/serverless/LLService.java | 993 ++++++++++++++++++ .../LLServiceConnectionListener.java | 32 + .../serverless/LLServiceDiscoveryManager.java | 563 ++++++++++ .../smack/serverless/LLServiceListener.java | 31 + .../serverless/LLServiceStateListener.java | 56 + .../smack/serverless/XMPPLLConnection.java | 567 ++++++++++ .../org/jivesoftware/smackx/MDNSListener.java | 40 + .../org/jivesoftware/smackx/TestMDNS.java | 267 +++++ 21 files changed, 3581 insertions(+), 10 deletions(-) create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/util/Tuple.java create mode 100644 smack-serverless/build.gradle create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSPresenceDiscoverer.java create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSService.java create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLConnectionConfiguration.java create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresence.java create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceDiscoverer.java create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceListener.java create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLService.java create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceConnectionListener.java create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceDiscoveryManager.java create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceListener.java create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceStateListener.java create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java create mode 100644 smack-serverless/src/test/java/org/jivesoftware/smackx/MDNSListener.java create mode 100644 smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java diff --git a/settings.gradle b/settings.gradle index 9c4bf13433..116ac9b37c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,4 +15,5 @@ include 'smack-core', 'smack-bosh', 'smack-android', 'smack-android-extensions', - 'smack-java7' + 'smack-java7', + 'smack-serverless' diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java index 039f6fcb7f..62b9cf339d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -50,6 +50,7 @@ public abstract class ConnectionConfiguration { protected final String host; protected final int port; + private final String keystorePath; private final String keystoreType; private final String pkcs11Library; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java b/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java index 21294e8aea..8f9b5c93d2 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java @@ -89,6 +89,10 @@ public void cancel() { } } + public boolean isCanceled() { + return cancelled; + } + /** * Returns the packet filter associated with this packet collector. The packet * filter is used to determine what packets are queued as results. diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/Tuple.java b/smack-core/src/main/java/org/jivesoftware/smack/util/Tuple.java new file mode 100644 index 0000000000..cbb8d97aec --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/Tuple.java @@ -0,0 +1,27 @@ +/** + * + * Copyright 2003-2014 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.util; + +public class Tuple { + public A a; + public B b; + + public Tuple(A a, B b) { + this.a = a; + this.b = b; + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java index c2487e9366..02febe09b5 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java @@ -46,6 +46,7 @@ import org.jivesoftware.smackx.xdata.packet.DataForm; import org.jxmpp.util.cache.LruCache; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -54,10 +55,12 @@ import java.util.Locale; import java.util.Map; import java.util.Queue; +import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.logging.Level; import java.util.logging.Logger; import java.security.MessageDigest; @@ -75,6 +78,7 @@ public class EntityCapsManager extends Manager { public static final String NAMESPACE = CapsExtension.NAMESPACE; public static final String ELEMENT = CapsExtension.ELEMENT; + public static final String DEFAULT_HASH = "sha-1"; private static final Map SUPPORTED_HASHES = new HashMap(); private static String DEFAULT_ENTITY_NODE = "http://www.igniterealtime.org/projects/smack"; @@ -104,6 +108,13 @@ public class EntityCapsManager extends Manager { */ private static final LruCache JID_TO_NODEVER_CACHE = new LruCache(10000); + /** + * Set of listeners to be notified when the local client's + * capabilities string is updated + */ + private final static Set capsVerListeners = + new CopyOnWriteArraySet(); + static { XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { public void connectionCreated(XMPPConnection connection) { @@ -112,13 +123,29 @@ public void connectionCreated(XMPPConnection connection) { }); try { - MessageDigest sha1MessageDigest = MessageDigest.getInstance("SHA-1"); - SUPPORTED_HASHES.put("sha-1", sha1MessageDigest); + MessageDigest sha1MessageDigest = MessageDigest.getInstance(DEFAULT_HASH); + SUPPORTED_HASHES.put(DEFAULT_HASH, sha1MessageDigest); } catch (NoSuchAlgorithmException e) { // Ignore } } + public interface CapsVerListener { + public void capsVerUpdated(String ver); + } + + public static void addCapsVerListener(CapsVerListener capsVerListener) { + capsVerListeners.add(capsVerListener); + } + + public static void removeCapsVerListener(CapsVerListener capsVerListener) { + capsVerListeners.remove(capsVerListener); + } + + public static Collection getCapsVerListeners() { + return Collections.unmodifiableCollection(capsVerListeners); + } + /** * Set the default entity node that will be used for new EntityCapsManagers * @@ -347,7 +374,7 @@ public void processPacket(Packet packet) { if (!entityCapsEnabled) return; - CapsExtension caps = new CapsExtension(entityNode, getCapsVersion(), "sha-1"); + CapsExtension caps = new CapsExtension(entityNode, getCapsVersion(), DEFAULT_HASH); packet.addExtension(caps); } }; @@ -391,6 +418,10 @@ public void setEntityNode(String entityNode) throws NotConnectedException { updateLocalEntityCaps(); } + public String getEntityNode() { + return entityNode; + } + /** * Remove a record telling what entity caps node a user has. * @@ -401,6 +432,15 @@ public void removeUserCapsNode(String user) { JID_TO_NODEVER_CACHE.remove(user); } + /** + * Add a record describing what enetity caps node a user has. + * + * @param user the user (Full JID) + */ + public void addUserCapsNode(String user, String node, String ver) { + JID_TO_NODEVER_CACHE.put(user, new NodeVerHash(node, ver, DEFAULT_HASH)); + } + /** * Get our own caps version. The version depends on the enabled features. A * caps version looks like '66/0NaeaBKkwk85efJTGmU47vXI=' @@ -464,7 +504,7 @@ public void updateLocalEntityCaps() { discoverInfo.setFrom(connection.getUser()); sdm.addDiscoverInfoTo(discoverInfo); - currentCapsVersion = generateVerificationString(discoverInfo, "sha-1"); + currentCapsVersion = generateVerificationString(discoverInfo, DEFAULT_HASH); addDiscoverInfoByNode(entityNode + '#' + currentCapsVersion, discoverInfo); if (lastLocalCapsVersions.size() > 10) { String oldCapsVersion = lastLocalCapsVersions.poll(); @@ -474,7 +514,7 @@ public void updateLocalEntityCaps() { CAPS_CACHE.put(currentCapsVersion, discoverInfo); if (connection != null) - JID_TO_NODEVER_CACHE.put(connection.getUser(), new NodeVerHash(entityNode, currentCapsVersion, "sha-1")); + JID_TO_NODEVER_CACHE.put(connection.getUser(), new NodeVerHash(entityNode, currentCapsVersion, DEFAULT_HASH)); final List identities = new LinkedList(ServiceDiscoveryManager.getInstanceFor(connection).getIdentities()); sdm.setNodeInformationProvider(entityNode + '#' + currentCapsVersion, new AbstractNodeInformationProvider() { @@ -494,7 +534,7 @@ public List getNodePacketExtensions() { } }); - // Send an empty presence, and let the packet intercepter + // Send an empty presence, and let the packet interceptor // add a node to it. // See http://xmpp.org/extensions/xep-0115.html#advertise // We only send a presence packet if there was already one send @@ -508,6 +548,10 @@ public List getNodePacketExtensions() { LOGGER.log(Level.WARNING, "Could could not update presence with caps info", e); } } + + for (CapsVerListener listener : getCapsVerListeners()) { + listener.capsVerUpdated(currentCapsVersion); + } } /** diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java index 0f7b96871a..f2b654dc5a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java @@ -80,7 +80,7 @@ public class ServiceDiscoveryManager extends Manager { private Set identities = new HashSet(); private DiscoverInfo.Identity identity = defaultIdentity; - private EntityCapsManager capsManager; + protected EntityCapsManager capsManager; private static Map instances = Collections.synchronizedMap(new WeakHashMap()); @@ -116,7 +116,7 @@ public static void setDefaultIdentity(DiscoverInfo.Identity identity) { * * @param connection the connection to which a ServiceDiscoveryManager is going to be created. */ - private ServiceDiscoveryManager(XMPPConnection connection) { + protected ServiceDiscoveryManager(XMPPConnection connection) { super(connection); addFeature(DiscoverInfo.NAMESPACE); @@ -759,7 +759,7 @@ public void setEntityCapsManager(EntityCapsManager manager) { /** * Updates the Entity Capabilities Verification String if EntityCaps is enabled. */ - private void renewEntityCapsVersion() { + protected void renewEntityCapsVersion() { if (capsManager != null && capsManager.entityCapsEnabled()) capsManager.updateLocalEntityCaps(); } diff --git a/smack-serverless/build.gradle b/smack-serverless/build.gradle new file mode 100644 index 0000000000..cd558b4a6e --- /dev/null +++ b/smack-serverless/build.gradle @@ -0,0 +1,8 @@ +description = """\ +XEP-0174 Serverless Messaging.""" + +dependencies { + compile 'javax.jmdns:jmdns:3.4.1' + compile project(':smack-core') + compile project(':smack-extensions') +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSPresenceDiscoverer.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSPresenceDiscoverer.java new file mode 100644 index 0000000000..5c0ae260c9 --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSPresenceDiscoverer.java @@ -0,0 +1,133 @@ +/** + * + * Copyright 2009 Jonas Ådahl. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack.serverless; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.util.Tuple; + +import javax.jmdns.JmDNS; +import javax.jmdns.ServiceEvent; +import javax.jmdns.ServiceListener; + +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.LinkedList; + + +/** + * An implementation of LLPresenceDiscoverer using JmDNS. + * + * @author Jonas Ådahl + */ +class JmDNSPresenceDiscoverer extends LLPresenceDiscoverer { + protected static final int SERVICE_REQUEST_TIMEOUT = 10000; + protected static JmDNS jmdns; + + JmDNSPresenceDiscoverer() throws XMPPException { + jmdns = JmDNSService.jmdns; + if (jmdns == null) + throw new XMPPException.XMPPErrorException(new XMPPError(XMPPError.Condition.undefined_condition, "Failed to fully initiate mDNS daemon.")); + + jmdns.addServiceListener(JmDNSService.SERVICE_TYPE, new PresenceServiceListener()); + } + + /** + * Convert raw TXT fields to a list of strings. + * The raw TXT fields are encoded as follows: + *
    + *
  • Byte 0 specifies the length of the first field (which starts at byte 1).
  • + *
  • If the last byte of the previous field is the last byte of the array, + * all TXT fields has been read.
  • + *
  • If there are more bytes following, the next byte after the last of the + * previous field specifies the length of the next field.
  • + *
+ * + * @param bytes raw TXT fields as an array of bytes. + * @return TXT fields as a list of strings. + */ + private static List TXTToList(byte[] bytes) { + List list = new LinkedList(); + int size_i = 0; + while (size_i < bytes.length) { + int s = (int)(bytes[size_i]); + try { + list.add(new String(bytes, ++size_i, s, "UTF-8")); + } catch (UnsupportedEncodingException uee) { + // ignore + } + size_i += s; + } + return list; + } + + /** + * Convert a TXT field list bundled with a '_presence._tcp' service to a + * String,String tuple. The TXT field list looks as following: + * "key=value" which is converted into the tuple (key, value). + * + * @param strings the TXT fields. + * @return a list of key,value tuples. + */ + private static List> TXTListToXMPPRecords(List strings) { + // records :: [(String, String)] + List> records = new LinkedList>(); + for (String s : strings) { + String[] record = s.split("=", 2); + // check if valid + if (record.length == 2) + records.add(new Tuple(record[0], record[1])); + } + return records; + } + + /** + * Implementation of a JmDNS ServiceListener. Listens to service resolved and + * service information resolved events. + */ + private class PresenceServiceListener implements ServiceListener { + public void serviceAdded(ServiceEvent event) { + // XXX + // To reduce network usage, we should only request information + // when needed. + new RequestInfoThread(event).start(); + } + public void serviceRemoved(ServiceEvent event) { + presenceRemoved(event.getName()); + } + public void serviceResolved(ServiceEvent event) { + presenceInfoAdded(event.getName(), + new LLPresence(event.getName(), + event.getInfo().getHostAddress(), event.getInfo().getPort(), + TXTListToXMPPRecords(TXTToList(event.getInfo().getTextBytes())))); + } + + private class RequestInfoThread extends Thread { + ServiceEvent event; + + RequestInfoThread(ServiceEvent event) { + this.event = event; + } + + public void run() { + jmdns.requestServiceInfo(event.getType(), event.getName(), + true, SERVICE_REQUEST_TIMEOUT); + } + } + } +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSService.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSService.java new file mode 100644 index 0000000000..47a5b38a9b --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSService.java @@ -0,0 +1,255 @@ +/** + * + * Copyright 2009 Jonas Ådahl. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack.serverless; + + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.util.Tuple; + +import javax.jmdns.JmDNS; +import javax.jmdns.ServiceEvent; +import javax.jmdns.ServiceInfo; +import javax.jmdns.ServiceListener; +import javax.jmdns.impl.JmDNSImpl; + +import java.net.InetAddress; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Map; + +/** + * Implements a LLService using JmDNS. + * + * @author Jonas Ådahl + */ +public class JmDNSService extends LLService implements ServiceListener { + static JmDNS jmdns = null; + private ServiceInfo serviceInfo; + static final String SERVICE_TYPE = "_presence._tcp.local."; + + private JmDNSService(LLPresence presence, LLPresenceDiscoverer presenceDiscoverer) { + super(presence, presenceDiscoverer); + } + + /** + * Instantiate a new JmDNSService and start to listen for connections. + * + * @param presence the mDNS presence information that should be used. + */ + public static LLService create(LLPresence presence) throws XMPPException { + return create(presence, null); + } + + /** + * Instantiate a new JmDNSService and start to listen for connections. + * + * @param presence the mDNS presence information that should be used. + * @param addr the INET Address to use. + */ + public static LLService create(LLPresence presence, InetAddress addr) throws XMPPException { + // Start the JmDNS daemon. + initJmDNS(addr); + + // Start the presence discoverer + JmDNSPresenceDiscoverer presenceDiscoverer = new JmDNSPresenceDiscoverer(); + + // Start the presence service + JmDNSService service = new JmDNSService(presence, presenceDiscoverer); + + return service; + } + + @Override + public void close() throws IOException { + super.close(); + jmdns.close(); + } + + /** + * Start the JmDNS daemon. + */ + private static void initJmDNS(InetAddress addr) throws XMPPException { + try { + if (jmdns == null) { + if (addr == null) { + jmdns = JmDNS.create(); + } + else { + jmdns = JmDNS.create(addr); + } + } + } + catch (IOException ioe) { + throw new XMPPException.XMPPErrorException("Failed to create a JmDNS instance", new XMPPError(XMPPError.Condition.undefined_condition), ioe); + } + } + + protected void updateText() { + Hashtable ht = new Hashtable(); + + for (Tuple t : presence.toList()) { + if (t.a != null && t.b != null) { + ht.put(t.a, t.b); + } + } + + serviceInfo.setText(ht); + } + + /** + * Register the DNS-SD service with the daemon. + */ + protected void registerService() throws XMPPException { + Hashtable ht = new Hashtable(); + + for (Tuple t : presence.toList()) { + if (t.a != null && t.b != null) + ht.put(t.a, t.b); + } + serviceInfo = ServiceInfo.create(SERVICE_TYPE, + presence.getServiceName(), presence.getPort(), 0, 0, ht); + jmdns.addServiceListener(SERVICE_TYPE, this); + try { + String originalServiceName = serviceInfo.getName(); + jmdns.registerService(serviceInfo); + String realizedServiceName = getRealizedServiceName(serviceInfo); + presence.setServiceName(realizedServiceName); + + if (!originalServiceName.equals(realizedServiceName)) { + serviceNameChanged(realizedServiceName, originalServiceName); + } + } + catch (IOException ioe) { + throw new XMPPException.XMPPErrorException("Failed to register DNS-SD Service", new XMPPError(XMPPError.Condition.undefined_condition), ioe); + } + } + + /** + * Reregister the DNS-SD service with the daemon. + * + * Note: This method does not accommodate changes to the mDNS Service Name! + * This method may be used to announce changes to the DNS TXT record. + */ + protected void reannounceService() throws XMPPException { + try { + jmdns.unregisterService(serviceInfo); + jmdns.registerService(serviceInfo); + // Note that because ServiceInfo objects are tracked + // within JmDNS by service name, if that value has changed + // we won't be able to successfully remove the 'old' service. + // Previously, jmdns exposed the following method: + //jmdns.reannounceService(serviceInfo); + } + catch (IOException ioe) { + throw new XMPPException.XMPPErrorException("Exception occured when reannouncing mDNS presence.", new XMPPError(XMPPError.Condition.undefined_condition), ioe); + } + } + + public void serviceNameChanged(String newName, String oldName) { + try { + super.serviceNameChanged(newName, oldName); + } + catch (Throwable t) { + // ignore + } + } + + /** + * Unregister the DNS-SD service, making the client unavailable. + */ + public void makeUnavailable() { + jmdns.unregisterService(serviceInfo); + serviceInfo = null; + } + + + @Override + public void spam() { + super.spam(); + System.out.println("Service name: " + serviceInfo.getName()); + } + + /** vv {@link javax.jmdns.ServiceListener} vv **/ + + @Override + public void serviceAdded(ServiceEvent event) { + // Calling super.serviceNameChanged changes + // the current local presence to that of the + // newly added Service. + // How can we assume that a new Service added + // corresponds to the local service name changing? + // This logic is currently executed when a new client joins + // changing the local presence and confusing our chat logic + // We could perhaps consider services added at the same host + // address to be name changes... But that also opens the + // door to undesired behavior when DHCP leases expire + // and local addresses are recycled + + // What's wrong with treating new services as new services? + // From my reading of XEP-0174, I don't see any reason why + // a client should change their service name. + +// System.out.println("Service added " + event.getName()); +// if (!presence.getServiceName().equals(event.getName())) { +// super.serviceNameChanged(event.getName(), presence.getServiceName()); +// } + } + + @Override + public void serviceRemoved(ServiceEvent event) { + + } + + @Override + public void serviceResolved(ServiceEvent event) { + + } + + /** ^^ {@link javax.jmdns.ServiceListener} ^^ **/ + + /** + * JmDNS may change the name of a requested service to enforce uniqueness + * within its DNS cache. This helper method can be called after {@link javax.jmdns.JmDNS#registerService(javax.jmdns.ServiceInfo)} + * with the passed {@link javax.jmdns.ServiceInfo} to attempt to determine the actual service + * name registered. e.g: "test@example" may become "test@example (2)" + * + * @param requestedInfo the ServiceInfo instance passed to {@link javax.jmdns.JmDNS#registerService(javax.jmdns.ServiceInfo)} + * @return the unique service name actually being advertised by JmDNS. If no + * match found, return requestedInfo.getName() + */ + private String getRealizedServiceName(ServiceInfo requestedInfo) { + Map map = ((JmDNSImpl) jmdns).getServices(); + // Check if requested service name is used verbatim + if (map.containsKey(requestedInfo.getKey())) { + return map.get(requestedInfo.getKey()).getName(); + } + + // The service name was altered... Search registered services + // e.g test@example.presence._tcp.local would match test@example (2).presence._tcp.local + for (ServiceInfo info : map.values()) { + if (info.getName().contains(requestedInfo.getName()) + && info.getTypeWithSubtype().equals(requestedInfo.getTypeWithSubtype())) { + return info.getName(); + } + } + + // No match found! Return expected name + return requestedInfo.getName(); + } +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLConnectionConfiguration.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLConnectionConfiguration.java new file mode 100644 index 0000000000..d24287717d --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLConnectionConfiguration.java @@ -0,0 +1,123 @@ +/** + * + * Copyright 2009 Jonas Ådahl. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack.serverless; + + +import org.jivesoftware.smack.ConnectionConfiguration; +import org.jivesoftware.smack.serverless.LLPresence; + +import javax.net.SocketFactory; +import java.net.Socket; + +/** + * Link-local connection configuration settings. Two general cases exists, + * one where the we want to connect to a remote peer, and one when o remote + * peer has connected to us. + */ +public class LLConnectionConfiguration extends ConnectionConfiguration implements Cloneable { + private static final String SERVICE_NAME = "locallink"; + private LLPresence remotePresence; + private LLPresence localPresence; + private Socket socket; + + /** + * Holds the socket factory that is used to generate the socket in the connection + */ + private SocketFactory socketFactory; + + /** + * Configuration used for connecting to remote peer. + * @param local LLPresence for the local user + * @param remote LLPresence for the remote user + */ + public LLConnectionConfiguration(LLPresence local, LLPresence remote) { + super(remote.getServiceName()); + this.localPresence = local; + this.remotePresence = remote; + } + + /** + * Instantiate a link-local configuration when the connection is acting as + * the host. + * + * @param local the local link-local presence class. + * @param remoteSocket the socket which the new connection is assigned to. + */ + public LLConnectionConfiguration(LLPresence local, Socket remoteSocket) { + super(null); + this.localPresence = local; + this.socket = remoteSocket; + } + + @Override + public void setServiceName(String serviceName) { + // ConnectionConfiguration#setServiceName extracts the domain from the serviceName + // e.g "david@guardian" -> "guardian" + // This is not the behavior we want for XEP-0174 clients + this.serviceName = serviceName; + } + + /** + * Tells if the connection is the initiating one. + * @return true if this configuration is for the connecting connection. + */ + public boolean isInitiator() { + return socket == null; + } + + /** + * Return the service name of the remote peer. + * @return the remote peer's service name. + */ + public String getRemoteServiceName() { + return remotePresence.getServiceName(); + } + + /** + * Return the service name of this client. + * @return this clients service name. + */ + public String getLocalServiceName() { + return localPresence.getServiceName(); + } + + /** + * Return this clients link-local presence information. + * @return this clients link-local presence information. + */ + public LLPresence getLocalPresence() { + return localPresence; + } + + /** + * Return the remote client's link-local presence information. + * @return the remote client's link-local presence information. + */ + public LLPresence getRemotePresence() { + return remotePresence; + } + + /** + * Return the socket which has been established when the + * remote client connected. + * @return the socket established when the remote client connected. + */ + public Socket getSocket() { + return socket; + } +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresence.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresence.java new file mode 100644 index 0000000000..02d22873cd --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresence.java @@ -0,0 +1,280 @@ +/** + * + * Copyright 2009 Jonas Ådahl. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack.serverless; + + +import org.jivesoftware.smack.util.Tuple; + +import java.util.List; +import java.util.LinkedList; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Class for describing a Link-local presence information according to XEP-0174. + * XEP-0174 describes how to represent XMPP presences using mDNS/DNS-SD. + * The presence information is stored as TXT fields; example from the documentation + * follows: + * juliet IN TXT "txtvers=1" + * juliet IN TXT "1st=Juliet" + * juliet IN TXT "email=juliet@capulet.lit" + * juliet IN TXT "hash=sha-1" + * juliet IN TXT "jid=juliet@capulet.lit" + * juliet IN TXT "last=Capulet" + * juliet IN TXT "msg=Hanging out downtown" + * juliet IN TXT "nick=JuliC" + * juliet IN TXT "node=http://www.adiumx.com" + * juliet IN TXT "phsh=a3839614e1a382bcfebbcf20464f519e81770813" + * juliet IN TXT "port.p2pj=5562" + * juliet IN TXT "status=avail" + * juliet IN TXT "vc=CA!" + * juliet IN TXT "ver=66/0NaeaBKkwk85efJTGmU47vXI=" + */ +public class LLPresence { + // Service info, gathered from the TXT fields + private String firstName, lastName, email, msg, nick, jid; + // caps version + private String hash, ver, node; + // XEP-0174 specifies that if status is not specified it is equal to "avail". + private Mode status = Mode.avail; + + // The unknown + private Map rest = + new ConcurrentHashMap(); + + public static enum Mode { + avail, away, dnd + } + + // Host details + private int port = 0; + private String host; + private String serviceName; + + public LLPresence(String serviceName) { + this.serviceName = serviceName; + } + + public LLPresence(String serviceName, String host, int port) { + this.serviceName = serviceName; + this.host = host; + this.port = port; + } + + public LLPresence(String serviceName, String host, int port, + List> records) { + this(serviceName, host, port); + + // Parse the tuple list (originating from the TXT fields) and put them + // in variables + for (Tuple t : records) { + if (t.a.equals("1st")) + setFirstName(t.b); + else if (t.a.equals("last")) + setLastName(t.b); + else if (t.a.equals("email")) + setEMail(t.b); + else if (t.a.equals("jid")) + setJID(t.b); + else if (t.a.equals("nick")) + setNick(t.b); + else if (t.a.equals("hash")) + setHash(t.b); + else if (t.a.equals("node")) + setNode(t.b); + else if (t.a.equals("ver")) + setVer(t.b); + else if (t.a.equals("status")) { + try { + setStatus(Mode.valueOf(t.b)); + } + catch (IllegalArgumentException iae) { + System.err.println("Found invalid presence status (" + + t.b + ") in TXT entry."); + } + } + else if (t.a.equals("msg")) + setMsg(t.b); + else { + // Unknown key + if (!rest.containsKey(t.a)) + rest.put(t.a, t.b); + } + } + } + + public List> toList() { + LinkedList> list = new LinkedList<>(); + list.add(new Tuple<>("txtvers", "1")); + list.add(new Tuple<>("1st", firstName)); + list.add(new Tuple<>("last", lastName)); + list.add(new Tuple<>("email", email)); + list.add(new Tuple<>("jid", jid)); + list.add(new Tuple<>("nick", nick)); + list.add(new Tuple<>("status", status.toString())); + list.add(new Tuple<>("msg", msg)); + list.add(new Tuple<>("hash", hash)); + list.add(new Tuple<>("node", node)); + list.add(new Tuple<>("ver", ver)); + list.add(new Tuple<>("port.p2ppj", Integer.toString(port))); + + for (Map.Entry e : rest.entrySet()) { + list.add(new Tuple<>(e.getKey(), e.getValue())); + } + + return list; + } + + /** + * Update all the values of the presence. + */ + void update(LLPresence p) { + setFirstName(p.getFirstName()); + setLastName(p.getLastName()); + setEMail(p.getEMail()); + setMsg(p.getMsg()); + setNick(p.getNick()); + setStatus(p.getStatus()); + setJID(p.getJID()); + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public void setFirstName(String name) { + firstName = name; + } + + public void setLastName(String name) { + lastName = name; + } + + public void setEMail(String email) { + this.email = email; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public void setNick(String nick) { + this.nick = nick; + } + + public void setStatus(Mode status) { + this.status = status; + } + + public void setJID(String jid) { + this.jid = jid; + } + + public void setHash(String hash) { + this.hash = hash; + } + + public void setNode(String node) { + this.node = node; + } + + public void setVer(String ver) { + this.ver = ver; + } + + void setPort(int port) { + this.port = port; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public String getEMail() { + return email; + } + + public String getMsg() { + return msg; + } + + public String getNick() { + return nick; + } + + public Mode getStatus() { + return status; + } + + public String getJID() { + return jid; + } + + public String getServiceName() { + return serviceName; + } + + public String getHost() { + return host; + } + + public String getHash() { + return hash; + } + + public String getNode() { + return node; + } + + public String getVer() { + return ver; + } + + public String getNodeVer() { + return node + "#" + hash; + } + + public int getPort() { + return port; + } + + public String getValue(String key) { + return rest.get(key); + } + + public void putValue(String key, String value) { + rest.put(key, value); + } + + public boolean equals(Object o) { + if (o instanceof LLPresence) { + LLPresence p = (LLPresence)o; + return p.serviceName == serviceName && + p.host == host; + } + return false; + } + + public int hashCode() { + return serviceName.hashCode(); + } +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceDiscoverer.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceDiscoverer.java new file mode 100644 index 0000000000..d010059404 --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceDiscoverer.java @@ -0,0 +1,109 @@ +/** + * + * Copyright 2009 Jonas Ådahl. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack.serverless; + + +import java.util.Set; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * Link-local presence discoverer. XEP-0174 describes how to use mDNS/DNS-SD. + * This class in an abstract representation of the basic functionality of + * handling presences discovering. + */ +public abstract class LLPresenceDiscoverer { + // Listeners to be notified about changes. + protected Set listeners = new CopyOnWriteArraySet(); + // Map of service name -> Link-local presence + private Map presences = new ConcurrentHashMap(); + + /** + * Add listener which will be notified when new presences are discovered, + * presence information changed or presences goes offline. + * @param listener the listener to be notified. + */ + public void addPresenceListener(LLPresenceListener listener) { + listeners.add(listener); + for (LLPresence presence : presences.values()) + listener.presenceNew(presence); + } + + /** + * Remove presence listener. + * @param listener listener to be removed. + */ + public void removePresenceListener(LLPresenceListener listener) { + listeners.remove(listener); + } + + /** + * Return a collection of presences known. + * @return all known presences. + */ + public Collection getPresences() { + return presences.values(); + } + + /** + * Return the presence with the specified service name. + * + * @param name service name of the presence. + * @return the presence information with the given service name. + */ + public LLPresence getPresence(String name) { + return presences.get(name); + } + + /** + * Used by the class extending this one to tell when new + * presence is added. + * + * @param name service name of the presence. + */ + protected void presenceAdded(String name) { + presences.put(name, null); + } + + /** + * Used by the class extending this one to tell when new + * presence information is added. + * + * @param name service name of the presence. + * @param presence presence information. + */ + protected void presenceInfoAdded(String name, LLPresence presence) { + presences.put(name, presence); + for (LLPresenceListener l : listeners) + l.presenceNew(presence); + } + + /** + * Used by the class extending this one to tell when a presence + * goes offline. + * + * @param name service name of the presence going offline. + */ + protected void presenceRemoved(String name) { + LLPresence presence = presences.remove(name); + for (LLPresenceListener l : listeners) + l.presenceRemove(presence); + } +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceListener.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceListener.java new file mode 100644 index 0000000000..e6ee0b9946 --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceListener.java @@ -0,0 +1,37 @@ +/** + * + * Copyright 2009 Jonas Ådahl. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack.serverless; + + +/** + * Interface for receiving notifications about presence changes. + */ +public interface LLPresenceListener { + /** + * New link-local presence has been discovered. + * + * @param presence information about the new presence + */ + + public void presenceNew(LLPresence presence); + /** + * A link-local presence has gone offline. + * @param presence the presence which went offline. + */ + public void presenceRemove(LLPresence presence); +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLService.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLService.java new file mode 100644 index 0000000000..e580b9095a --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLService.java @@ -0,0 +1,993 @@ +/** + * + * Copyright 2009 Jonas Ådahl. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack.serverless; + + +import org.jivesoftware.smack.AbstractConnectionListener; +import org.jivesoftware.smack.Chat; +import org.jivesoftware.smack.ChatManager; +import org.jivesoftware.smack.ChatManagerListener; +import org.jivesoftware.smack.ConnectionListener; +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.filter.PacketFilter; +import org.jivesoftware.smack.filter.AndFilter; +import org.jivesoftware.smack.filter.OrFilter; +import org.jivesoftware.smack.filter.PacketIDFilter; +import org.jivesoftware.smack.filter.IQTypeFilter; +import org.jivesoftware.smack.packet.XMPPError; + +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.Map; +import java.util.ArrayList; +import java.util.UUID; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ConcurrentHashMap; + +/** + * LLService acts as an abstract interface to a Link-local XMPP service + * according to XEP-0174. XEP-0174 describes how this is achieved using + * mDNS/DNS-SD, and this class creates an implementation unspecific + * interface for doing this. + * + * The mDNS/DNS-SD is for example implemented by JmDNSService (using JmDNS). + * + * There is only one instance of LLService possible at one time. + * + * Tasks taken care of here are: + *
    + *
  • Connection Management + *
      + *
    • Keep track of active connections from and to the local client
    • + *
    • Listen for connections on a semi randomized port announced by the + * mDNS/DNS-SD daemon
    • + *
    • Establish new connections when there is none to use and packets are + * to be sent
    • + *
        + *
      • Chat Management - Keep track of messaging sessions between users
      • + *
      + * + * @author Jonas Ådahl + */ +public abstract class LLService { + private static LLService service = null; + + // Listeners for new services + private static Set serviceCreatedListeners = + new CopyOnWriteArraySet(); + + static final int DEFAULT_MIN_PORT = 2300; + static final int DEFAULT_MAX_PORT = 2400; + protected LLPresence presence; + private boolean done = false; + private Thread listenerThread; + + private boolean initiated = false; + + private Map chats = + new ConcurrentHashMap(); + + private Map ingoing = + new ConcurrentHashMap(); + private Map outgoing = + new ConcurrentHashMap(); + + // Listeners for state updates, such as LLService closed down + private Set stateListeners = + new CopyOnWriteArraySet(); + + // Listeners for XMPPLLConnections associated with this service + private Set llServiceConnectionListeners = + new CopyOnWriteArraySet(); + + // Listeners for packets coming from this Link-local service + private final Map listeners = + new ConcurrentHashMap(); + + // Presence discoverer, notifies of changes in presences on the network. + private LLPresenceDiscoverer presenceDiscoverer; + + // chat listeners gets notified when new chats are created + private Set chatListeners = new CopyOnWriteArraySet<>(); + + // Set of Packet collector wrappers + private Set collectorWrappers = + new CopyOnWriteArraySet(); + + // Set of associated connections. + private Set associatedConnections = + new HashSet(); + + private ServerSocket socket; + + static { + SmackConfiguration.getVersion(); + } + + /** + * Spam stdout with some debug information. + */ + public void spam() { + System.out.println("Number of ingoing connection in map: " + ingoing.size()); + System.out.println("Number of outgoing connection in map: " + outgoing.size()); + + System.out.println("Active chats:"); +// for (LLChat chat : chats.values()) { +// System.out.println(" * " + chat.getServiceName()); +// } + + System.out.println("Known presences:"); + for (LLPresence presence : presenceDiscoverer.getPresences()) { + System.out.println(" * " + presence.getServiceName() + "(" + presence.getStatus() + ", " + presence.getHost() + ":" + presence.getPort() + ")"); + } + Thread.currentThread().getThreadGroup().list(); + } + + protected LLService(LLPresence presence, LLPresenceDiscoverer discoverer) { + this.presence = presence; + presenceDiscoverer = discoverer; + service = this; + + XMPPLLConnection.addLLConnectionListener(new AbstractConnectionListener() { + + @Override + public void connected(XMPPConnection xmppConnection) { + if (! (xmppConnection instanceof XMPPLLConnection)) { + return; + } + XMPPLLConnection connection = (XMPPLLConnection) xmppConnection; + // We only care about this connection if we were the one + // creating it + if (isAssociatedConnection(connection)) { + if (connection.isInitiator()) { + addOutgoingConnection(connection); + } + else { + addIngoingConnection(connection); + } + + connection.addConnectionListener(new ConnectionActivityListener(connection)); + + // Notify listeners that a new connection associated with this + // service has been created. + notifyNewServiceConnection(connection); + + + // add other existing packet filters associated with this service + for (ListenerWrapper wrapper : listeners.values()) { + connection.addPacketListener(wrapper.getPacketListener(), + wrapper.getPacketFilter()); + } + + // add packet collectors + for (CollectorWrapper cw : collectorWrappers) { + cw.createPacketCollector(connection); + } + } + } + }); + + notifyServiceListeners(this); + } + + /** + * Add a LLServiceListener. The LLServiceListener is notified when a new + * Link-local service is created. + * + * @param listener the LLServiceListener + */ + public static void addLLServiceListener(LLServiceListener listener) { + serviceCreatedListeners.add(listener); + } + + /** + * Remove a LLServiceListener. + */ + public static void removeLLServiceListener(LLServiceListener listener) { + serviceCreatedListeners.remove(listener); + } + + /** + * Notify LLServiceListeners about a new Link-local service. + * + * @param service the new service. + */ + public static void notifyServiceListeners(LLService service) { + for (LLServiceListener listener : serviceCreatedListeners) { + listener.serviceCreated(service); + } + } + + /** + * Returns the running mDNS/DNS-SD XMPP instance. There can only be one + * instance at a time. + * + * @return the active LLService instance. + * @throws XMPPException if the LLService hasn't been instantiated. + */ + public synchronized static LLService getServiceInstance() throws XMPPException { + if (service == null) + throw new XMPPException.XMPPErrorException("Link-local service not initiated.", + new XMPPError(XMPPError.Condition.undefined_condition)); + return service; + } + + /** + * Registers the service to the mDNS/DNS-SD daemon. + * Should be implemented by the class extending this, for mDNS/DNS-SD library specific calls. + */ + protected abstract void registerService() throws XMPPException; + + /** + * Re-announce the presence information by using the mDNS/DNS-SD daemon. + */ + protected abstract void reannounceService() throws XMPPException; + + /** + * Make the client unavailabe. Equivalent to sending unavailable-presence. + */ + public abstract void makeUnavailable(); + + /** + * Update the text field information. Used for setting new presence information. + */ + protected abstract void updateText(); + + public void init() throws XMPPException { + // allocate a new port for remote clients to connect to + socket = bindRange(DEFAULT_MIN_PORT, DEFAULT_MAX_PORT); + presence.setPort(socket.getLocalPort()); + + // register service on the allocated port + registerService(); + + // start to listen for new connections + listenerThread = new Thread() { + public void run() { + try { + // Listen for connections + listenForConnections(); + + // If listen for connections returns with no exception, + // the service has closed down + for (LLServiceStateListener listener : stateListeners) + listener.serviceClosed(); + } catch (XMPPException e) { + for (LLServiceStateListener listener : stateListeners) + listener.serviceClosedOnError(e); + } + } + }; + listenerThread.setName("Smack Link-local Service Listener"); + listenerThread.setDaemon(true); + listenerThread.start(); + + initiated = true; + } + + public void close() throws IOException { + done = true; + + // close incoming connections + for (XMPPLLConnection connection : ingoing.values()) { + try { + connection.shutdown(); + } catch (Exception e) { + // ignore + } + } + + // close outgoing connections + for (XMPPLLConnection connection : outgoing.values()) { + try { + connection.shutdown(); + } catch (Exception e) { + // ignore + } + } + try { + socket.close(); + } catch (IOException ioe) { + // ignore + } + } + + /** + * Listen for new connections on socket, and spawn XMPPLLConnections + * when new connections are established. + * + * @throws XMPPException whenever an exception occurs + */ + private void listenForConnections() throws XMPPException { + while (!done) { + try { + // wait for new connection + Socket s = socket.accept(); + + LLConnectionConfiguration config = + new LLConnectionConfiguration(presence, s); + XMPPLLConnection connection = new XMPPLLConnection(this, config); + + // Associate the new connection with this service + addAssociatedConnection(connection); + + // Spawn new thread to handle the connecting. + // The reason for spawning a new thread is to let two remote clients + // be able to connect at the same time. + Thread connectionInitiatorThread = + new ConnectionInitiatorThread(connection); + connectionInitiatorThread.setName("Smack Link-local Connection Initiator"); + connectionInitiatorThread.setDaemon(true); + connectionInitiatorThread.start(); + } + catch (SocketException se) { + // If we are closing down, it's probably closed socket exception. + if (!done) { + throw new XMPPException.XMPPErrorException("Link-local service unexpectedly closed down.", + new XMPPError(XMPPError.Condition.undefined_condition), se); + } + } + catch (IOException ioe) { + throw new XMPPException.XMPPErrorException("Link-local service unexpectedly closed down.", + new XMPPError(XMPPError.Condition.undefined_condition), ioe); + } + } + } + + /** + * Bind one socket to any port within a given range. + * + * @param min the minimum port number allowed + * @param max hte maximum port number allowed + * @throws XMPPException if binding failed on all allowed ports. + */ + private static ServerSocket bindRange(int min, int max) throws XMPPException { + int port = 0; + for (int try_port = min; try_port <= max; try_port++) { + try { + ServerSocket socket = new ServerSocket(try_port); + return socket; + } + catch (IOException e) { + // failed to bind, try next + } + } + throw new XMPPException.XMPPErrorException("Unable to bind port, no ports available.", + new XMPPError(XMPPError.Condition.resource_constraint)); + } + + protected void unknownOriginMessage(Message message) { + for (LLServiceStateListener listener : stateListeners) { + listener.unknownOriginMessage(message); + } + } + + protected void serviceNameChanged(String newName, String oldName) { + // update our own presence with the new name, for future connections + presence.setServiceName(newName); + + // clean up connections + XMPPLLConnection c; + c = getConnectionTo(oldName); + if (c != null) + c.disconnect(); + c = getConnectionTo(newName); + if (c != null) + c.disconnect(); + + // notify listeners + for (LLServiceStateListener listener : stateListeners) { + listener.serviceNameChanged(newName, oldName); + } + } + + /** + * Adds a listener that are notified when a new link-local connection + * has been established. + * + * @param listener A class implementing the LLConnectionListener interface. + */ + public void addLLServiceConnectionListener(LLServiceConnectionListener listener) { + llServiceConnectionListeners.add(listener); + } + + /** + * Removes a listener from the new connection listener list. + * + * @param listener The class implementing the LLConnectionListener interface that + * is to be removed. + */ + public void removeLLServiceConnectionListener(LLServiceConnectionListener listener) { + llServiceConnectionListeners.remove(listener); + } + + private void notifyNewServiceConnection(XMPPLLConnection connection) { + for (LLServiceConnectionListener listener : llServiceConnectionListeners) { + listener.connectionCreated(connection); + } + } + + /** + * Add the given connection to the list of associated connections. + * An associated connection means it's a Link-Local connection managed + * by this service. + * + * @param connection the connection to be associated + */ + private void addAssociatedConnection(XMPPLLConnection connection) { + synchronized (associatedConnections) { + associatedConnections.add(connection); + } + } + + /** + * Remove the given connection from the list of associated connections. + * + * @param connection the connection to be removed. + */ + private void removeAssociatedConnection(XMPPLLConnection connection) { + synchronized (associatedConnections) { + associatedConnections.remove(connection); + } + } + + /** + * Return true if the given connection is associated / managed by this + * service. + * + * @param connection the connection to be checked + * @return true if the connection is associated with this service or false + * if it is not associated with this service. + */ + private boolean isAssociatedConnection(XMPPLLConnection connection) { + synchronized (associatedConnections) { + return associatedConnections.contains(connection); + } + } + + /** + * Add a packet listener. + * + * @param listener the PacketListener + * @param filter the Filter + */ + public void addPacketListener(PacketListener listener, PacketFilter filter) { + ListenerWrapper wrapper = new ListenerWrapper(listener, filter); + listeners.put(listener, wrapper); + + // Also add to existing connections + synchronized (ingoing) { + synchronized (outgoing) { + for (XMPPLLConnection c : getConnections()) { + c.addPacketListener(listener, filter); + } + } + } + } + + /** + * Remove a packet listener. + */ + public void removePacketListener(PacketListener listener) { + listeners.remove(listener); + + // Also add to existing connections + synchronized (ingoing) { + synchronized (outgoing) { + for (XMPPLLConnection c : getConnections()) { + c.removePacketListener(listener); + } + } + } + } + + /** + * Add service state listener. + * + * @param listener the service state listener to be added. + */ + public void addServiceStateListener(LLServiceStateListener listener) { + stateListeners.add(listener); + } + + /** + * Remove service state listener. + * + * @param listener the service state listener to be removed. + */ + public void removeServiceStateListener(LLServiceStateListener listener) { + stateListeners.remove(listener); + } + +// /** +// * Add Link-local chat session listener. The chat session listener will +// * be notified when new link-local chat sessions are created. +// * +// * @param listener the listener to be added. +// */ +// public void addLLChatListener(ChatManagerListener listener) { +// chatListeners.add(listener); +// } +// +// /** +// * Remove Link-local chat session listener. +// * +// * @param listener the listener to be removed. +// */ +// public void removeLLChatListener(ChatManagerListener listener) { +// chatListeners.remove(listener); +// } + + /** + * Add presence listener. A presence listener will be notified of new + * presences, presences going offline, and changes in presences. + * + * @param listener the listener to be added. + */ + public void addPresenceListener(LLPresenceListener listener) { + presenceDiscoverer.addPresenceListener(listener); + } + + /** + * Remove presence listener. + * + * @param listener presence listener to be removed. + */ + public void removePresenceListener(LLPresenceListener listener) { + presenceDiscoverer.removePresenceListener(listener); + } + + /** + * Get the presence information associated with the given service name. + * + * @param serviceName the service name which information should be returned. + * @return the service information. + */ + public LLPresence getPresenceByServiceName(String serviceName) { + return presenceDiscoverer.getPresence(serviceName); + } + + public CollectorWrapper createPacketCollector(PacketFilter filter) { + CollectorWrapper wrapper = new CollectorWrapper(filter); + collectorWrappers.add(wrapper); + return wrapper; + } + + /** + * Return a collection of all active connections. This may be used if the + * user wants to change a property on all connections, such as add a service + * discovery feature or other. + * + * @return a colllection of all active connections. + */ + public Collection getConnections() { + Collection connections = + new ArrayList(outgoing.values()); + connections.addAll(ingoing.values()); + return connections; + } + + /** + * Returns a connection to a given service name. + * First checks for an outgoing connection, if noone exists, + * try ingoing. + * + * @param serviceName the service name + * @return a connection associated with the service name or null if no + * connection is available. + */ + XMPPLLConnection getConnectionTo(String serviceName) { + XMPPLLConnection connection = outgoing.get(serviceName); + if (connection != null) + return connection; + return ingoing.get(serviceName); + } + + void addIngoingConnection(XMPPLLConnection connection) { + ingoing.put(connection.getServiceName(), connection); + } + + void removeIngoingConnection(XMPPLLConnection connection) { + ingoing.remove(connection.getServiceName()); + } + + void addOutgoingConnection(XMPPLLConnection connection) { + outgoing.put(connection.getServiceName(), connection); + } + + void removeOutgoingConnection(XMPPLLConnection connection) { + outgoing.remove(connection.getServiceName()); + } + +// LLChat removeLLChat(String serviceName) { +// return chats.remove(serviceName); +// } +// +// /** +// * Returns a new {@link org.jivesoftware.smack.serverless.LLChat} +// * at the request of the local client. +// * This method should not be used to create Chat sessions +// * in response to messages received from remote peers. +// */ +// void newLLChat(LLChat chat) { +// chats.put(chat.getServiceName(), chat); +// for (ChatManagerListener listener : chatListeners) { +// listener.chatCreated(chat, true); +// } +// } + + /** + * Get a LLChat associated with a given service name. + * If no LLChat session is available, a new one is created. + * + * This method should not be used to create Chat sessions + * in response to messages received from remote peers. + * + * @param serviceName the service name + * @return a chat session instance associated with the given service name. + */ + public Chat getChat(String serviceName) throws XMPPException, IOException, SmackException { + Chat chat = chats.get(serviceName); + if (chat == null) { + LLPresence presence = getPresenceByServiceName(serviceName); + if (presence == null) + throw new XMPPException.XMPPErrorException("Can't initiate new chat to '" + + serviceName + "': mDNS presence unknown.", new XMPPError(XMPPError.Condition.undefined_condition)); + chat =ChatManager.getInstanceFor(service.getConnection(presence.getServiceName())).createChat( + presence.getServiceName(), + UUID.randomUUID().toString(), null); + //newLLChat(chat); + } + return chat; + } + + /** + * Returns a XMPPLLConnection to the serviceName. + * If no established connection exists, a new connection is created. + * + * @param serviceName Service name of the remote client. + * @return A connection to the given service name. + */ + public XMPPLLConnection getConnection(String serviceName) throws XMPPException.XMPPErrorException, IOException, SmackException { + // If a connection exists, return it. + XMPPLLConnection connection = getConnectionTo(serviceName); + if (connection != null) + return connection; + + // If no connection exists, look up the presence and connect according to. + LLPresence remotePresence = getPresenceByServiceName(serviceName); + + if (remotePresence == null) { + throw new XMPPException.XMPPErrorException("Can't initiate connection, remote peer is not available.", + new XMPPError(XMPPError.Condition.recipient_unavailable)); + } + + LLConnectionConfiguration config = + new LLConnectionConfiguration(presence, remotePresence); + connection = new XMPPLLConnection(this, config); + // Associate the new connection with this service + addAssociatedConnection(connection); + connection.connect(); + addOutgoingConnection(connection); + + return connection; + } + + /** + * Send a message to the remote peer. + * + * @param message the message to be sent. + * @throws XMPPException if the message cannot be sent. + */ + void sendMessage(Message message) throws XMPPException, IOException, SmackException { + sendPacket(message); + } + + + /** + * Send a packet to the remote peer. + * + * @param packet the packet to be sent. + * @throws XMPPException if the packet cannot be sent. + */ + public void sendPacket(Packet packet) throws XMPPException, IOException, SmackException { + getConnection(packet.getTo()).sendPacket(packet); + } + + /** + * Send an IQ set or get and wait for the response. This function works + * different from a normal one-connection IQ request where a packet + * collector is created and added to the connection. This function + * takes care of (at least) two cases when this doesn't work: + *
        + *
      • Consider client A requests something from B. This is done by + * A connecting to B (no existing connection is available), then + * sending an IQ request to B using the new connection and starts + * waiting for a reply. However the connection between them may be + * terminated due to inactivity, and for B to reply, it have to + * establish a new connection. This function takes care of this + * by listening for the packets on all new connections.
      • + *
      • Consider client A and client B concurrently establishes + * connections between them. This will result in two parallell + * connections between the two entities and the two clients may + * choose whatever connection to use when communicating. This + * function takes care of the possibility that if A requests + * something from B using connection #1 and B replies using + * connection #2, the packet will still be collected.
      • + *
      + */ + public IQ getIQResponse(IQ request) throws XMPPException, IOException, SmackException { + XMPPLLConnection connection = getConnection(request.getTo()); + + // Create a packet collector to listen for a response. + // Filter: req.id == rpl.id ^ (rp.iqtype in (result, error)) + CollectorWrapper collector = createPacketCollector( + new AndFilter( + new PacketIDFilter(request.getPacketID()), + new OrFilter( + IQTypeFilter.RESULT, + IQTypeFilter.ERROR))); + + connection.sendPacket(request); + + // Wait up to 5 seconds for a result. + IQ result = (IQ) collector.nextResult( + SmackConfiguration.getDefaultPacketReplyTimeout()); + + // Stop queuing results + collector.cancel(); + if (result == null) { + throw new XMPPException.XMPPErrorException("No response from the remote host.", + new XMPPError(XMPPError.Condition.undefined_condition)); + } + + return result; + } + + /** + * Update the presence information announced by the mDNS/DNS-SD daemon. + * The presence object stored in the LLService class will be updated + * with the new information and the daemon will reannounce the changes. + * + * @param presence the new presence information + * @throws XMPPException if an error occurs + */ + public void updateLocalPresence(LLPresence presence) throws XMPPException { + this.presence.update(presence); + + if (initiated) { + updateText(); + reannounceService(); + } + } + + /** + * Get current Link-local presence. + */ + public LLPresence getLocalPresence() { + return presence; + } + + /** + * ConnectionActivityListener listens for link-local connection activity + * such as closed connection and broken connection, and keeps record of + * what active connections exist up to date. + */ + private class ConnectionActivityListener implements ConnectionListener { + private XMPPLLConnection connection; + + ConnectionActivityListener(XMPPLLConnection connection) { + this.connection = connection; + } + + @Override + public void connected(XMPPConnection connection) { + + } + + @Override + public void authenticated(XMPPConnection connection) { + + } + + public void connectionClosed() { + removeConnectionRecord(); + } + + public void connectionClosedOnError(Exception e) { + removeConnectionRecord(); + } + + public void reconnectingIn(int seconds) { + } + + public void reconnectionSuccessful() { + } + + public void reconnectionFailed(Exception e) { + } + + private void removeConnectionRecord() { + if (connection.isInitiator()) + removeOutgoingConnection(connection); + else + removeIngoingConnection(connection); + + removeAssociatedConnection(connection); + } + } + + /** + * Initiates a connection in a seperate thread, controlling + * it was established correctly and stream was initiated. + */ + private class ConnectionInitiatorThread extends Thread { + XMPPLLConnection connection; + + ConnectionInitiatorThread(XMPPLLConnection connection) { + this.connection = connection; + } + + public void run() { + try { + connection.initListen(); + } + catch (XMPPException | SmackException | IOException e) { + // ignore, since its an incoming connection + // there is nothing to save + } + } + } + + /** + * A wrapper class to associate a packet filter with a listener. + */ + private static class ListenerWrapper { + + private PacketListener packetListener; + private PacketFilter packetFilter; + + public ListenerWrapper(PacketListener packetListener, PacketFilter packetFilter) { + this.packetListener = packetListener; + this.packetFilter = packetFilter; + } + + public void notifyListener(Packet packet) throws SmackException.NotConnectedException { + if (packetFilter == null || packetFilter.accept(packet)) { + packetListener.processPacket(packet); + } + } + + public PacketListener getPacketListener() { + return packetListener; + } + + public PacketFilter getPacketFilter() { + return packetFilter; + } + } + + /** + * Packet Collector Wrapper which is used for collecting packages + * from multiple connections as well as newly established connections (works + * together with LLService constructor. + */ + public class CollectorWrapper { + // Existing collectors. + private Set collectors = + new CopyOnWriteArraySet(); + + // Packet filter for all the collectors. + private PacketFilter packetFilter; + + // A common object used for shared locking between + // the collectors. + private Object lock = new Object(); + + private CollectorWrapper(PacketFilter packetFilter) { + this.packetFilter = packetFilter; + + // Apply to all active connections + for (XMPPLLConnection connection : getConnections()) { + createPacketCollector(connection); + } + } + + /** + * Create a new per-connection packet collector. + * + * @param connection the connection the collector should be added to. + */ + private void createPacketCollector(XMPPLLConnection connection) { + synchronized (connection) { + PacketCollector collector = + connection.createPacketCollector(packetFilter); + //collector.setLock(lock); + collectors.add(collector); + } + } + + /** + * Returns the next available packet. The method call will block (not return) + * until a packet is available or the timeout has elapsed. If the + * timeout elapses without a result, null will be returned. + * + * @param timeout the amount of time to wait for the next packet + * (in milleseconds). + * @return the next available packet. + */ + public synchronized Packet nextResult(long timeout) { + Packet packet; + long waitTime = timeout; + long start = System.currentTimeMillis(); + + try { + while (true) { + for (PacketCollector c : collectors) { + if (c.isCanceled()) + collectors.remove(c); + else { + packet = c.pollResult(); + if (packet != null) + return packet; + } + } + + if (waitTime <= 0) { + break; + } + + // TODO: lock won't be notified bc it's no longer managed by PacketCollector + // Perhaps we need a different mechanism here + // wait + synchronized (lock) { + lock.wait(waitTime); + } + long now = System.currentTimeMillis(); + waitTime -= (now - start); + } + } + catch (InterruptedException ie) { + // ignore + } + + return null; + } + + public void cancel() { + for (PacketCollector c : collectors) { + c.cancel(); + } + collectorWrappers.remove(this); + } + } +} + diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceConnectionListener.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceConnectionListener.java new file mode 100644 index 0000000000..de655155ac --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceConnectionListener.java @@ -0,0 +1,32 @@ +/** + * + * Copyright 2003-2014 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.serverless; + + +/** + * Notification about when new Link-local connections associated with a + * specific Link-local service has been established. + */ +public interface LLServiceConnectionListener { + + /** + * A new link-local connection has been established. + * + * @param connection the new established connection. + */ + public void connectionCreated(XMPPLLConnection connection); +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceDiscoveryManager.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceDiscoveryManager.java new file mode 100644 index 0000000000..1459ae2845 --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceDiscoveryManager.java @@ -0,0 +1,563 @@ +/** + * + * Copyright 2009 Jonas Ådahl. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack.serverless; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +import org.jivesoftware.smack.AbstractConnectionListener; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smackx.disco.NodeInformationProvider; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.caps.EntityCapsManager; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; +import org.jivesoftware.smackx.disco.packet.DiscoverItems; +import org.jivesoftware.smackx.xdata.packet.DataForm; + +/** + * LLServiceDiscoveryManager acts as a wrapper around ServiceDiscoveryManager + * as ServiceDiscoveryManager only creates an interface for requesting service + * information on existing connections. Simply said it creates new connections + * when needed, uses already active connections when appropriate and applies + * values to new connections. + * + * @author Jonas Ådahl + */ +public class LLServiceDiscoveryManager extends ServiceDiscoveryManager { + private static Map serviceManagers = + new ConcurrentHashMap(); + + private LLService service; + + /* + We'll create a new LLServiceDiscoveryManager each time a new XMPPLLConnection + is created. The issue with the above attempt to create a LLServiceDiscoveryManager + on each LLService creation is that, by my reading, we must have both an XMPPLLConnection + and LLService to construct a meaningful LLServiceDiscoveryManager. + + If a client would like to specify features to be advertised in advance of an + XMPPLLConnection being created, they should register those features with + ServiceDiscoveryManager#addDefaultFeature(String) + This way we manage advertised features in one spot, not per individual XMPPLLConnection. + Perhaps an even better solution would be for each LLService to manage the list of features + to be provided to each LLServiceDiscoveryManager whenever an XMPPLLConnection is initiated. + Please let me know (dbro@dbro.pro) if you've any thoughts on this matter. + */ + static { + XMPPLLConnection.addLLConnectionListener(new AbstractConnectionListener() { + + @Override + public void connected(XMPPLLConnection connection) { + addLLServiceDiscoveryManager(getInstanceFor(connection)); + } + }); + } + + protected LLServiceDiscoveryManager(LLService llservice, XMPPConnection connection) { + super(connection); + this.service = llservice; + + + + // Add LLService state listener + service.addServiceStateListener(new LLServiceStateListener() { + private void removeEntry() { + removeLLServiceDiscoveryManager(service); + } + + public void serviceClosed() { + removeEntry(); + } + + public void serviceClosedOnError(Exception e) { + removeEntry(); + } + + public void unknownOriginMessage(Message e) { + // ignore + } + + public void serviceNameChanged(String n, String o) { + // mDNS service names should not change after connections + // are established, so may remove this logic + + // Remove entries + capsManager.removeUserCapsNode(n); + capsManager.removeUserCapsNode(o); + LLPresence np = service.getPresenceByServiceName(n); + LLPresence op = service.getPresenceByServiceName(o); + + // Add existing values, if any + if (np != null && np.getNode() != null && np.getVer() != null){ + capsManager.addUserCapsNode(n, np.getNode(), np.getVer()); + } + if (op != null && op.getNode() != null && op.getVer() != null) + capsManager.addUserCapsNode(o, op.getNode(), op.getVer()); + } + }); + + // Entity Capabilities + capsManager = EntityCapsManager.getInstanceFor(connection); + EntityCapsManager.addCapsVerListener(new CapsPresenceRenewer()); + // Provide EntityCaps features, identities & node to own DiscoverInfo +// capsManager.calculateEntityCapsVersion(getOwnDiscoverInfo(), +// getIdentityType(), +// getIdentityName(), +// extendedInfo); + + capsManager.updateLocalEntityCaps(); + + + // Add presence listener. The presence listener will gather + // entity caps data + service.addPresenceListener(new LLPresenceListener() { + public void presenceNew(LLPresence presence) { + if (presence.getHash() != null && + presence.getNode() != null && + presence.getVer() != null) { + // Add presence to caps manager + capsManager.addUserCapsNode(presence.getServiceName(), + presence.getNode(), presence.getVer()); + } + } + + public void presenceRemove(LLPresence presence) { + + } + }); + + service.addLLServiceConnectionListener(new ConnectionServiceMaintainer()); + } + + /** + * Add LLServiceDiscoveryManager to the map of existing ones. + */ + private static void addLLServiceDiscoveryManager(LLServiceDiscoveryManager manager) { + serviceManagers.put(manager.service, manager); + } + + /** + * Remove LLServiceDiscoveryManager from the map of existing ones. + */ + private static void removeLLServiceDiscoveryManager(LLService service) { + serviceManagers.remove(service); + } + + /** + * Get the LLServiceDiscoveryManager instance for a specific Link-local service. + * + * @param service + */ + public static LLServiceDiscoveryManager getInstanceFor(LLService service) { + return serviceManagers.get(service); + } + + public static LLServiceDiscoveryManager getInstanceFor(XMPPLLConnection connection) { + LLServiceDiscoveryManager llsdm = serviceManagers.get(connection.getService()); + if (llsdm == null) { + llsdm = new LLServiceDiscoveryManager(connection.getService(), connection); + } + return llsdm; + } + +// When would we change the Identity type? +// use ServiceDiscoveryManager#setIdentity(Identity) +// /** +// * Sets the type of client that will be returned when asked for the client identity in a +// * disco request. The valid types are defined by the category client. Follow this link to learn +// * the possible types: Jabber::Registrar. +// * +// * @param type the type of client that will be returned when asked for the client identity in a +// * disco request. +// */ +// public static void setIdentityType(String type) { +// ServiceDiscoveryManager.setIdentityType(type); +// } + + /** + * Add discover info response data. + * + * @param response the discover info response packet + */ + @Override + public void addDiscoverInfoTo(DiscoverInfo response) { + // Set this client identity + DiscoverInfo.Identity identity = new DiscoverInfo.Identity("client", + getIdentityName(), getIdentityType()); + response.addIdentity(identity); + // Add the registered features to the response + // Add Entity Capabilities (XEP-0115) feature node. + response.addFeature("http://jabber.org/protocol/caps"); + + for (String feature : getFeatures()) { + response.addFeature(feature); + } + if (extendedInfo != null) { + response.addExtension(extendedInfo); + } + } + + /** + * Get a DiscoverInfo for the current entity caps node. + * + * @return a DiscoverInfo for the current entity caps node + */ + public DiscoverInfo getOwnDiscoverInfo() { + DiscoverInfo di = new DiscoverInfo(); + di.setType(IQ.Type.result); + di.setNode(capsManager.getLocalNodeVer()); + + // Add discover info + addDiscoverInfoTo(di); + + for (String feature : features) { + di.addFeature(feature); + } + + return di; + } + + /** + * Returns a new or already established connection to the given service name. + * + * @param serviceName remote service to which we wish to be connected to. + * @returns an established connection to the given service name. + */ + private XMPPLLConnection getConnection(String serviceName) throws XMPPException.XMPPErrorException, IOException, SmackException { + return service.getConnection(serviceName); + } + + /** + * Returns a ServiceDiscoveryManager instance for a new or already established + * connection to the given service name. + * + * @param serviceName the name of the service we wish to get the ServiceDiscoveryManager instance for. + * @returns the ServiceDiscoveryManager instance. + */ + private ServiceDiscoveryManager getInstance(String serviceName) throws SmackException, IOException, XMPPException.XMPPErrorException { + return ServiceDiscoveryManager.getInstanceFor(getConnection(serviceName)); + } + + /** + * Registers extended discovery information of this XMPP entity. When this + * client is queried for its information this data form will be returned as + * specified by XEP-0128. + *

      + * + * Since no packet is actually sent to the server it is safe to perform this + * operation before logging to the server. In fact, you may want to + * configure the extended info before logging to the server so that the + * information is already available if it is required upon login. + * + * @param info + * the data form that contains the extend service discovery + * information. + */ + @Override + public void setExtendedInfo(DataForm info) { + extendedInfo = info; + + // set for already active connections + for (XMPPLLConnection connection : service.getConnections()) + ServiceDiscoveryManager.getInstanceFor(connection).setExtendedInfo(info); + + renewEntityCapsVersion(); + } + + /** + * Removes the dataform containing extended service discovery information + * from the information returned by this XMPP entity.

      + * + * Since no packet is actually sent to the server it is safe to perform this + * operation before logging to the server. + */ + @Override + public void removeExtendedInfo() { + extendedInfo = null; + + // remove for already active connections + for (XMPPLLConnection connection : service.getConnections()) + ServiceDiscoveryManager.getInstanceFor(connection).removeExtendedInfo(); + + renewEntityCapsVersion(); + } + + /** + * Returns the discovered information of a given XMPP entity addressed by its JID and + * note attribute. Use this message only when trying to query information which is not + * directly addressable. + * + * @param serviceName the service name of the XMPP entity. + * @param node the attribute that supplements the 'jid' attribute. + * @return the discovered information. + * @throws XMPPException if the operation failed for some reason. + */ + @Override + public DiscoverInfo discoverInfo(String serviceName, String node) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException { + // Discover the entity's info + DiscoverInfo disco = new DiscoverInfo(); + disco.setType(IQ.Type.get); + disco.setTo(serviceName); + disco.setNode(node); + + IQ result = null; + try { + result = service.getIQResponse(disco); + } catch (XMPPException | IOException | SmackException e) { + throw new SmackException.NoResponseException(); + } + if (result == null) { + throw new XMPPException.XMPPErrorException("No response from the server.", new XMPPError(XMPPError.Condition.remote_server_timeout)); + } + if (result.getType() == IQ.Type.error) { + throw new XMPPException.XMPPErrorException(result.getError()); + } + if (result instanceof DiscoverInfo) { + return (DiscoverInfo) result; + } + throw new XMPPException.XMPPErrorException("Result was not a disco info reply.", new XMPPError(XMPPError.Condition.undefined_condition)); + } + + /** + * Returns the discovered items of a given XMPP entity addressed by its JID and + * note attribute. Use this message only when trying to query information which is not + * directly addressable. + * + * @param serviceName the service name of the XMPP entity. + * @param node the attribute that supplements the 'jid' attribute. + * @return the discovered items. + * @throws XMPPException if the operation failed for some reason. + */ + @Override + public DiscoverItems discoverItems(String serviceName, String node) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException { + // Discover the entity's items + DiscoverItems disco = new DiscoverItems(); + disco.setType(IQ.Type.get); + disco.setTo(serviceName); + disco.setNode(node); + + IQ result = null; + try { + result = service.getIQResponse(disco); + } catch (XMPPException | IOException | SmackException e) { + throw new SmackException.NoResponseException(); + } + if (result == null) { + throw new XMPPException.XMPPErrorException("No response from the server.", new XMPPError(XMPPError.Condition.remote_server_timeout)); + } + if (result.getType() == IQ.Type.error) { + throw new XMPPException.XMPPErrorException(result.getError()); + } + if (result instanceof DiscoverInfo) { + return (DiscoverItems) result; + } + throw new XMPPException.XMPPErrorException("Result was not a disco info reply.", new XMPPError(XMPPError.Condition.undefined_condition)); + } + + /** + * Sets the NodeInformationProvider responsible for providing information + * (ie items) related to a given node. Every time this client receives a disco request + * regarding the items of a given node, the provider associated to that node will be the + * responsible for providing the requested information.

      + * + * In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the + * NodeInformationProvider will provide information about the rooms where the user has joined. + * + * @param node the node whose items will be provided by the NodeInformationProvider. + * @param listener the NodeInformationProvider responsible for providing items related + * to the node. + */ + @Override + public void setNodeInformationProvider(String node, + NodeInformationProvider listener) { + super.setNodeInformationProvider(node, listener); + + // set for already active connections + Collection connections = service.getConnections(); + for (XMPPLLConnection connection : connections) + ServiceDiscoveryManager.getInstanceFor(connection).setNodeInformationProvider(node, listener); + } + + /** + * Removes the NodeInformationProvider responsible for providing information + * (ie items) related to a given node. This means that no more information will be + * available for the specified node. + * + * In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the + * NodeInformationProvider will provide information about the rooms where the user has joined. + * + * @param node the node to remove the associated NodeInformationProvider. + */ + @Override + public void removeNodeInformationProvider(String node) { + super.removeNodeInformationProvider(node); + + // remove from existing connections + for (XMPPLLConnection connection : service.getConnections()) + ServiceDiscoveryManager.getInstanceFor(connection).removeNodeInformationProvider(node); + } + + /** + * Removes the specified feature from the supported features set for all XMPPLL entities.

      + * + * Since no packet is actually sent to the server it is safe to perform this operation + * before logging to the server. + * + * @param feature the feature to remove from the supported features. + */ + @Override + public void removeFeature(String feature) { + for (XMPPLLConnection connection : service.getConnections()) + ServiceDiscoveryManager.getInstanceFor(connection).removeFeature(feature); + + super.removeFeature(feature); + } + + + /** + * Returns true if the specified feature is registered in the ServiceDiscoveryManager. + * + * @param feature the feature to look for. + * @return a boolean indicating if the specified featured is registered or not. + */ + @Override + public boolean includesFeature(String feature) { + return features.contains(feature); + } + + /** + * Returns true if the server supports publishing of items. A client may wish to publish items + * to the server so that the server can provide items associated to the client. These items will + * be returned by the server whenever the server receives a disco request targeted to the bare + * address of the client (i.e. user@host.com). + * + * @param entityID the address of the XMPP entity. + * @return true if the server supports publishing of items. + * @throws XMPPException if the operation failed for some reason. + */ + @Override + public boolean canPublishItems(String entityID) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException { + DiscoverInfo info = discoverInfo(entityID); + return ServiceDiscoveryManager.canPublishItems(info); + } + + /** + * Publishes new items to a parent entity. The item elements to publish MUST have at least + * a 'jid' attribute specifying the Entity ID of the item, and an action attribute which + * specifies the action being taken for that item. Possible action values are: "update" and + * "remove". + * + * @param entityID the address of the XMPP entity. + * @param discoverItems the DiscoveryItems to publish. + * @throws XMPPException if the operation failed for some reason. + */ + @Override + public void publishItems(String entityID, DiscoverItems discoverItems) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException { + publishItems(entityID, null, discoverItems); + } + + /** + * Publishes new items to a parent entity and node. The item elements to publish MUST have at + * least a 'jid' attribute specifying the Entity ID of the item, and an action attribute which + * specifies the action being taken for that item. Possible action values are: "update" and + * "remove". + * + * @param entityID the address of the XMPP entity. + * @param node the attribute that supplements the 'jid' attribute. + * @param discoverItems the DiscoveryItems to publish. + * @throws XMPPException if the operation failed for some reason. + */ + @Override + public void publishItems(String entityID, String node, DiscoverItems discoverItems) + throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException { + try { + getInstance(entityID).publishItems(entityID, node, discoverItems); + } catch (XMPPException | SmackException | IOException e) { + e.printStackTrace(); + // An exception specific to the Serverless stack occurred. + // IOException : We were unable to complete #getConnection in LLService at XMPPLLConnection#connect() + // + throw new SmackException.NotConnectedException(); + } + } + + private String getEntityCapsVersion() { + if (capsManager != null) { + return capsManager.getCapsVersion(); + } + else { + return null; + } + } + + + /** + * In case that a connection is unavailable we create a new connection + * and push the service discovery procedure until the new connection is + * established. + */ + private class ConnectionServiceMaintainer implements LLServiceConnectionListener { + + public void connectionCreated(XMPPLLConnection connection) { + // Add service discovery for Link-local connections.\ + ServiceDiscoveryManager manager = ServiceDiscoveryManager.getInstanceFor(connection); + + // Set Entity Capabilities Manager + manager.setEntityCapsManager(capsManager); + + // Set extended info + manager.setExtendedInfo(extendedInfo); + + // Set node information providers + for (Map.Entry entry : + nodeInformationProviders.entrySet()) { + manager.setNodeInformationProvider(entry.getKey(), entry.getValue()); + } + + // add features + for (String feature : features) { + manager.addFeature(feature); + } + } + } + + private class CapsPresenceRenewer implements EntityCapsManager.CapsVerListener { + public void capsVerUpdated(String ver) { + synchronized (service) { + try { + LLPresence presence = service.getLocalPresence(); + presence.setHash(EntityCapsManager.DEFAULT_HASH); + presence.setNode(capsManager.getEntityNode()); + presence.setVer(ver); + service.updateLocalPresence(presence); + } + catch (XMPPException xe) { + // ignore + } + } + } + } +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceListener.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceListener.java new file mode 100644 index 0000000000..653d526074 --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceListener.java @@ -0,0 +1,31 @@ +/** + * + * Copyright 2003-2014 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.serverless; + + +/** + * Notification for new Link-local services created. + */ +public interface LLServiceListener { + + /** + * The function called when a new Link-local service is created. + * + * @param service the new service + */ + public void serviceCreated(LLService service); +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceStateListener.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceStateListener.java new file mode 100644 index 0000000000..000290ae10 --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceStateListener.java @@ -0,0 +1,56 @@ +/** + * + * Copyright 2003-2014 Jive Software. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.serverless; + + +import org.jivesoftware.smack.packet.Message; + +/** + * Interface for handeling link-local service events such as + * service closing, service crashes and other events. + */ +public interface LLServiceStateListener { + + /** + * Notification that the service name was changed. + * + * @param newName the new service name + * @param oldName the previous service name + */ + public void serviceNameChanged(String newName, String oldName); + + /** + * Notification that the connection was closed normally. + */ + public void serviceClosed(); + + /** + * Notification that the connection was closed due to an exception. + * + * @param e the exception. + */ + public void serviceClosedOnError(Exception e); + + /** + * Notification that a message with unknown presence was received. + * This could be someone being invisible, meaning no presece is + * announced. + * + * @param e the exception. + */ + public void unknownOriginMessage(Message e); +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java new file mode 100644 index 0000000000..3218542779 --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java @@ -0,0 +1,567 @@ +/** + * + * Copyright 2009 Jonas Ådahl. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.serverless; + + +import org.jivesoftware.smack.AbstractXMPPConnection; +import org.jivesoftware.smack.ConnectionListener; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.SmackException.NotConnectedException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.PlainStreamElement; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.tcp.XMPPTCPConnection; +import org.jivesoftware.smack.tcp.XMPPTCPConnection.PacketReader; +import org.jivesoftware.smack.tcp.XMPPTCPConnection.PacketWriter; +import org.xmlpull.v1.XmlPullParser; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.Date; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + + +/** + * Link-local XMPP connection according to XEP-0174 connection. Automatically + * created by LLService and closed by inactivity. + * + */ +public class XMPPLLConnection extends AbstractXMPPConnection { + + private LLService service; + private LLPresence localPresence, remotePresence; + private boolean initiator; + private long lastActivity = 0; + // FIXME this should become a boolean, it's purpose is to detect "inactive" connections + protected XMPPLLConnection connection; + private Thread timeoutThread; + private Socket socket; + + /** + * Protected access level because of unit test purposes + */ + protected Object packetWriter; + + /** + * Protected access level because of unit test purposes + */ + protected Object packetReader; + + /** + * Instantiate a new link-local connection. Use the config parameter to + * specify if the connection is acting as server or client. + * + * @param config specification about how the new connection is to be set up. + */ + XMPPLLConnection(LLService service, LLConnectionConfiguration config) { + super(config); + // Always append the "from" attribute + setFromMode(FromMode.USER); + connection = this; + this.service = service; + updateLastActivity(); + + // A timeout thread's purpose is to close down inactive connections + // after a certain amount of seconds (defaults to 15). + timeoutThread = new Thread() { + public void run() { + try { + while (connection != null) { + //synchronized (connection) { + Thread.sleep(14000); + long currentTime = new Date().getTime(); + if (currentTime - lastActivity > 15000) { + shutdown(); + break; + } + //} + } + } catch (InterruptedException ie) { + shutdown(); + } + } + }; + + timeoutThread.setName("Smack Link-local Connection Timeout (" + connection.connectionCounterValue + ")"); + timeoutThread.setDaemon(true); + + // Move to LLConnectionConfiguration#init + if (config.isInitiator()) { + // we are connecting to remote host + localPresence = config.getLocalPresence(); + remotePresence = config.getRemotePresence(); + initiator = true; + } else { + // a remote host connected to us + localPresence = config.getLocalPresence(); + remotePresence = null; + initiator = false; + socket = config.getSocket(); + } + } + + /** + * Return this connection's LLService + */ + public LLService getService() { + return service; + } + + /** + * Tells if this connection instance is the initiator. + * + * @return true if this instance is the one connecting to a remote peer. + */ + public boolean isInitiator() { + return initiator; + } + + /** + * Return the user name of the remote peer (service name). + * + * @return the remote hosts service name / username + */ + public String getUser() { + // username is the service name of the local presence + return localPresence.getServiceName(); + } + + /** + * Sets the name of the service provided in the from the remote peer. + * + * @param serviceName the name of the service + */ + public void setServiceName(String serviceName) { + ((LLConnectionConfiguration)config).setServiceName(serviceName); + //((LLConnectionConfiguration)config).setServiceName(remotePresence.getServiceName()); + //LLConnectionConfiguration llconfig = new LLConnectionConfiguration(localPresence, remotePresence); + //llconfig.setServiceName("Test"); + + } + + + /** + * Set the remote presence. Used when being connected, + * will not know the remote service name until stream is initiated. + * + * @param remotePresence presence information about the connecting client. + */ + void setRemotePresence(LLPresence remotePresence) { + this.remotePresence = remotePresence; + } + + /** + * Start listen for data and a stream tag. + */ + void initListen() throws XMPPException, IOException, SmackException { + initConnection(); + } + + /** + * Create a socket, connect to the remote peer and initiate a XMPP stream session. + */ + public void connect() throws IOException, SmackException, XMPPException.XMPPErrorException { + String host = remotePresence.getHost(); + int port = remotePresence.getPort(); + + try { + socket = new Socket(host, port); + } + catch (UnknownHostException uhe) { + String errorMessage = "Could not connect to " + host + ":" + port + "."; + throw new XMPPException.XMPPErrorException(errorMessage, new XMPPError( + XMPPError.Condition.remote_server_timeout, errorMessage), + uhe); + } + catch (IOException ioe) { + String errorMessage = "Error connecting to " + host + ":" + + port + "."; + throw new XMPPException.XMPPErrorException(errorMessage, new XMPPError( + XMPPError.Condition.remote_server_error, errorMessage), ioe); + } + initConnection(); + + notifyLLListenersConnected(); + } + + + /** + * Handles the opening of a stream after a remote client has connected and opened a stream. + * @throws XMPPException if service name is missing or service is unknown to the mDNS daemon. + */ + public void streamInitiatingReceived() throws XMPPException { + if (config.getServiceName() == null) { + shutdown(); + } else { + packetWriter = new LLPacketWriter(); + if (debugger != null) { + if (debugger.getWriterListener() != null) { + addPacketListener(debugger.getWriterListener(), null); + } + } + packetWriter.startup(); + notifyLLListenersConnected(); + } + } + + /** + * Notify new connection listeners that a new connection has been established. + */ + private void notifyLLListenersConnected() { + for (ConnectionListener listener : getConnectionListeners()) { + listener.connected(this); + } + } + + /** + * Update the timer telling when the last activity happend. Used by timeout + * thread to tell how long the connection has been inactive. + */ + void updateLastActivity() { + lastActivity = new Date().getTime(); + } + + /** + * Sends the specified packet to the remote peer. + * + * @param packet the packet to send + */ + @Override + public void sendPacket(Packet packet) throws SmackException.NotConnectedException { + updateLastActivity(); + super.sendPacket(packet); + } + + /** + * Initializes the connection by creating a packet reader and writer and opening a + * XMPP stream to the server. + * + * @throws XMPPException if establishing a connection to the server fails. + */ + private void initConnection() throws IOException, XMPPException.XMPPErrorException, SmackException { + try { + // Set the reader and writer instance variables + initReaderAndWriter(); + timeoutThread.start(); + // Don't initialize packet writer until we know it's a valid connection + // unless we are the initiator. If we are NOT the initializer, we instead + // wait for a stream initiation before doing anything. + if (isInitiator()) + packetWriter = new LLPacketWriter(); + + // Initialize packet reader + packetReader = new LLPacketReader(); + + // If debugging is enabled, we should start the thread that will listen for + // all packets and then log them. + // XXX FIXME debugging enabled not working + if (false) {//configuration.isDebuggerEnabled()) { + addPacketListener(debugger.getReaderListener(), null); + } + + // Make note of the fact that we're now connected. + connected = true; + + // If we are the initiator start the packet writer. This will open a XMPP + // stream to the server. If not, a packet writer will be started after + // receiving an initial stream start tag. + if (isInitiator()) + packetWriter.startup(); + // Start the packet reader. The startup() method will block until we + // get an opening stream packet back from server. + packetReader.startup(); + } + catch (XMPPException.XMPPErrorException | IOException | SmackException ex) { + // An exception occurred in setting up the connection. Make sure we shut down the + // readers and writers and close the socket. + + shutdownPacketReadersAndWritersAndCloseSocket(); + + throw ex; // Everything stopped. Now throw the exception. + } + } + + private void shutdownPacketReadersAndWritersAndCloseSocket() { + if (packetWriter != null) { + try { + packetWriter.shutdown(); + } + catch (Throwable ignore) { /* ignore */ } + packetWriter = null; + } + if (packetReader != null) { + try { + packetReader.shutdown(); + } + catch (Throwable ignore) { /* ignore */ } + packetReader = null; + } + if (socket != null) { + try { + socket.close(); + } + catch (Exception e) { /* ignore */ } + socket = null; + } + // closing reader after socket since reader.close() blocks otherwise + if (reader != null) { + try { + reader.close(); + } + catch (Throwable ignore) { /* ignore */ } + reader = null; + } + if (writer != null) { + try { + writer.close(); + } + catch (Throwable ignore) { /* ignore */ } + writer = null; + } + connected = false; + } + + private void initReaderAndWriter() throws XMPPException.XMPPErrorException { + try { + reader = + new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8")); + writer = new BufferedWriter( + new OutputStreamWriter(socket.getOutputStream(), "UTF-8")); + } + catch (IOException ioe) { + throw new XMPPException.XMPPErrorException( + "XMPPError establishing connection with server.", + new XMPPError(XMPPError.Condition.remote_server_error, + "XMPPError establishing connection with server."), + ioe); + } + + // If debugging is enabled, we open a window and write out all network traffic. + initDebugger(); + } + + protected void shutdown() { + connection = null; + + if (packetReader != null) + packetReader.shutdown(); + if (packetWriter != null) + packetWriter.shutdown(); + + // Wait 150 ms for processes to clean-up, then shutdown. + try { + Thread.sleep(150); + } + catch (Exception e) { + // Ignore. + } + + // Close down the readers and writers. + if (reader != null) { + try { + reader.close(); + } + catch (Throwable ignore) { /* ignore */ } + reader = null; + } + if (writer != null) { + try { + writer.close(); + } + catch (Throwable ignore) { /* ignore */ } + writer = null; + } + + try { + socket.close(); + } + catch (Exception e) { + // Ignore. + } + } + + public void disconnect() { + // If not connected, ignore this request. + if (packetReader == null || packetWriter == null) { + return; + } + + shutdown(); + + packetWriter = null; + packetReader = null; + } + + protected class LLPacketReader extends PacketReader { + + private boolean mGotStreamOpenedStanza = false; + + LLPacketReader() throws SmackException { + } + + public synchronized void startup() throws IOException, SmackException { + readerThread.start(); + + try { + // Wait until either: + // - the remote peer's stream initialization stanza has been parsed + // - an exception is thrown while parsing + // - the timeout occurs + if (connection.isInitiator()) + wait(getPacketReplyTimeout()); + } + catch (InterruptedException ie) { + // Ignore. + ie.printStackTrace(); + } + if (connection.isInitiator() && !mGotStreamOpenedStanza) { + throwConnectionExceptionOrNoResponse(); + } + } + + @Override + protected void handleStreamOpened(XmlPullParser parser) throws Exception { + super.handleStreamOpened(parser); + + // if we are the initiator, this means stream has been initiated + // if we aren't the initiator, this means we have to respond with + // stream initiator. + if (connection.isInitiator()) { + mGotStreamOpenedStanza = true; + connection.connectionID = connection.getServiceName(); + //releaseConnectionIDLock(); + } + else { + // Check if service name is a known entity + // if it is, open the stream and keep it open + // otherwise open and immediately close it + if (connection.getServiceName() == null) { + System.err.println("No service name specified in stream initiation, canceling."); + shutdown(); + } else { + // Check if service name is known, if so + // we will continue the session + LLPresence presence = service.getPresenceByServiceName(connection.getServiceName()); + if (presence != null) { + connection.setRemotePresence(presence); + connectionID = connection.getServiceName(); + connection.streamInitiatingReceived(); + //releaseConnectionIDLock(); + } else { + System.err.println("Unknown service name '" + + connection.getServiceName() + + "' specified in stream initation, canceling."); + shutdown(); + } + } + } + } + } + + protected class LLPacketWriter extends PacketWriter { + + + @Override + protected void openStream() throws IOException { + // Unlike traditional XMPP Stream initiation, + // we must provide our XEP-0174 Service Name + // in a "from" attribute + StringBuilder stream = new StringBuilder(); + stream.append(""); + writer.write(stream.toString()); + writer.flush(); + } + } + + @Override + public String getConnectionID() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isAuthenticated() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isSecureConnection() { + // TODO Auto-generated method stub + return false; + } + + @Override + protected void sendPacketInternal(Packet packet) + throws NotConnectedException { + // TODO Auto-generated method stub + + } + + @Override + public void send(PlainStreamElement element) throws NotConnectedException { + // TODO Auto-generated method stub + + } + + @Override + public boolean isUsingCompression() { + // TODO Auto-generated method stub + return false; + } + + @Override + protected void connectInternal() throws SmackException, IOException, + XMPPException { + // TODO Auto-generated method stub + + } + + @Override + public void login(String username, String password, String resource) + throws XMPPException, SmackException, IOException { + // TODO Auto-generated method stub + + } + + @Override + public void loginAnonymously() throws XMPPException, SmackException, + IOException { + // TODO Auto-generated method stub + + } +} diff --git a/smack-serverless/src/test/java/org/jivesoftware/smackx/MDNSListener.java b/smack-serverless/src/test/java/org/jivesoftware/smackx/MDNSListener.java new file mode 100644 index 0000000000..f1796fab1c --- /dev/null +++ b/smack-serverless/src/test/java/org/jivesoftware/smackx/MDNSListener.java @@ -0,0 +1,40 @@ +/** + * + * Copyright 2009 Jonas Ådahl. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx; + +import org.jivesoftware.smack.serverless.LLPresence; +import org.jivesoftware.smack.serverless.LLPresenceListener; + +public class MDNSListener implements LLPresenceListener { + + public void presenceNew(LLPresence pr) { + try { + System.out.println("New presence: " + pr.getServiceName() + + " (" + pr.getStatus() + "), ver=" + pr.getVer()); + } catch (Exception e) { + System.err.println(e); + e.printStackTrace(); + } + + } + + public void presenceRemove(LLPresence pr) { + System.out.println("Removed presence: " + pr.getServiceName()); + } + +} diff --git a/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java b/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java new file mode 100644 index 0000000000..f4d25310bf --- /dev/null +++ b/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java @@ -0,0 +1,267 @@ +/** + * + * Copyright 2009 Jonas Ådahl. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx; + +import org.jivesoftware.smack.ChatManagerListener; +import org.jivesoftware.smack.MessageListener; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.serverless.JmDNSService; +import org.jivesoftware.smack.serverless.LLChat; +import org.jivesoftware.smack.serverless.LLPresence; +import org.jivesoftware.smack.serverless.LLService; +import org.jivesoftware.smack.serverless.LLServiceDiscoveryManager; +import org.jivesoftware.smack.serverless.LLServiceStateListener; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; + +//import javax.jmdns.impl.SocketListener; + +public class TestMDNS { + LLService service; + public static void main(String[] argv) { + SmackConfiguration.DEBUG_ENABLED = true; + Handler ch = new ConsoleHandler(); + ch.setLevel(Level.FINEST); + //System.out.println(ConsoleHandler.class.getName()); + //Logger.global.addHandler(ch); + Logger.getLogger("").addHandler(ch); + //Logger.getLogger("").setLevel(Level.FINEST); + //Logger.global.setLevel(Level.FINEST); + //Logger.getLogger("javax.jmdns.impl.JmDNSImpl").addHandler(ch); + //Logger.getLogger("javax.jmdns.impl.SocketListener").setLevel(Level.FINEST); + Logger.getLogger("javax.jmdns").log(Level.FINE, "Fine?"); + Logger.getLogger("javax.jmdns").log(Level.WARNING, "Warning?"); + TestMDNS test = new TestMDNS(); + test.run(); + } + + public void run() { + try { + // Initiate stdin buffer for reading commands (the fancy UI) + BufferedReader stdIn = new BufferedReader( + new InputStreamReader(System.in)); + + // Create some kind of user name + String name = "smack-mdns@localhost"; + try { + name = System.getenv("USERNAME") + "@" + java.net.InetAddress.getLocalHost().getHostName(); + } catch (Exception e) {} + + System.out.println("Link-local presence name set to '" + name + "'"); + // Create a basic presence (only set name, and status to available) + LLPresence presence = new LLPresence(name); + System.out.println("Initiating Link-local service..."); + // Create a XMPP Link-local service. + service = JmDNSService.create(presence); + service.addServiceStateListener(new LLServiceStateListener() { + public void serviceNameChanged(String newName, String oldName) { + System.out.println("Service named changed from " + oldName + " to " + newName + ""); + } + + public void serviceClosed() { + System.out.println("Service closed"); + } + + public void serviceClosedOnError(Exception e) { + System.out.println("Service closed due to an exception"); + e.printStackTrace(); + } + + public void unknownOriginMessage(Message m) { + System.out.println("This message has unknown origin:"); + System.out.println(m.toXML()); + } + }); + + // Adding presence listener. + service.addPresenceListener(new MDNSListener()); + + System.out.println("Prepering link-local service discovery..."); + + // Note that an LLServiceDiscoveryManager is not created + // until an actual XMPPLLConnection is created. Instead we now + // specify default features to LLServiceDiscoveryManager's static methods + // As we want to play with service discovery, initiate the wrapper + //LLServiceDiscoveryManager disco = LLServiceDiscoveryManager.getInstanceFor(service); + +// if (disco == null) { +// System.err.println("Failed to initiate Service Discovery Manager."); +// System.exit(1); +// } + + System.out.println("Adding three default features to service discovery manager..."); + LLServiceDiscoveryManager.addDefaultFeature("http://www.jabber.org/extensions/lalal"); + LLServiceDiscoveryManager.addDefaultFeature("http://www.jabber.org/extenions/thetetteet"); + LLServiceDiscoveryManager.addDefaultFeature("urn:xmpp:hejhoppextension"); + + // Start listen for Link-local chats + service.addLLChatListener(new MyChatListener()); + + // Add hook for doing a clean shut down + Runtime.getRuntime().addShutdownHook(new CloseDownService(service)); + + // Initiate Link-local message session + service.init(); + + // Implement a user friendly interface. + String line; + boolean done = false; + + System.out.println("Welcome to the Smack Link-local sample client interface!"); + System.out.println("========================================================"); + while (!done) { + try { + System.out.print("> "); + line = stdIn.readLine(); + if ("quit".equals(line)) + done = true; + else if ("spam".equals(line)) { + service.spam(); + } + else if ("msg".equals(line)) { + System.out.print("Enter user: "); + String user = stdIn.readLine(); + System.out.print("Enter message: "); + String message = stdIn.readLine(); + LLChat chat = service.getChat(user); + chat.sendMessage(message); + System.out.println("Message sent."); + } + else if ("addfeature".equals(line)) { + System.out.print("Enter new feature: "); + String feature = stdIn.readLine(); + LLServiceDiscoveryManager.addDefaultFeature(feature); + } + else if ("disco".equals(line)) { + System.out.print("Enter user service name e.g (dave@service): "); + String user = stdIn.readLine(); + DiscoverInfo info = LLServiceDiscoveryManager.getInstanceFor(service).discoverInfo(user); + System.out.println(" # Discovered: " + info.toXML()); + } + else if ("status".equals(line)) { + System.out.print("Enter new status: "); + String status = stdIn.readLine(); + try { + presence.setStatus(LLPresence.Mode.valueOf(status)); + service.updateLocalPresence(presence); + } + catch (IllegalArgumentException iae) { + System.err.println("Illegal status: " + status); + } + } + } + catch (XMPPException xe) { + System.out.println("Caught XMPPException: " + xe); + xe.printStackTrace(); + //done = true; + } + catch (IOException ioe) { + System.out.println("Caught IOException: " + ioe); + ioe.printStackTrace(); + done = true; + } catch (SmackException.NotConnectedException e) { + System.out.println("Caught NotConnectedException: " + e); + e.printStackTrace(); + } catch (SmackException.NoResponseException e) { + System.out.println("Caught NoResponseException: " + e); + e.printStackTrace(); + } catch (SmackException e) { + System.out.println("Caught SmackException: " + e); + e.printStackTrace(); + } + } + System.exit(0); + } catch (XMPPException xe) { + System.err.println(xe); + } + } + + private class CloseDownService extends Thread { + LLService service; + + public CloseDownService(LLService service) { + this.service = service; + } + + public void run () { + System.out.println("### Unregistering service...."); + //service.makeUnavailable(); + System.out.println("### Done, now closing daemon..."); + + try { Thread.sleep(1000); } catch (Exception e) { } + try { + service.close(); + } catch (IOException e) { + System.out.println("Error closing service"); + e.printStackTrace(); + } + System.out.println("### Done."); + try { Thread.sleep(2000); } catch (Exception e) { } + Thread.currentThread().getThreadGroup().list(); + } + } + + private class MyChatListener implements ChatManagerListener { + public MyChatListener() {} + + @Override + public void chatCreated(LLChat chat, boolean createdLocally) { + System.out.println("Discovered new chat being created."); + chat.addMessageListener(new MyMessageListener(chat)); + } + } + + private class MyMessageListener implements MessageListener { + LLChat chat; + + MyMessageListener(LLChat chat) { + this.chat = chat; + } + + @Override + public void processMessage(LLChat chat, Message message) { + try { + if (message.getBody().equals("ping")) { + chat.sendMessage("pong"); + System.out.println("### received a ping, replied with pong."); + } + else if (message.getBody().equals("spam")) { + service.spam(); + } + else { + System.out.println("### <" + chat.getParticipant() + + "> " + message.getBody()); + } + } + catch (SmackException | XMPPException xe) { + System.out.println("Caught XMPPException in message listener: " + xe); + xe.printStackTrace(); + } + } + } +} From 9a891e512576126ce98e4ff2e8da3268c0dcde0a Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 2 Dec 2014 10:27:06 +0100 Subject: [PATCH 2/8] Moved serverless classes into subpackages --- .../smack/serverless/LLServiceDiscoveryManager.java | 5 ++++- .../org/jivesoftware/smack/serverless/XMPPLLConnection.java | 1 + .../serverless/{ => service}/LLPresenceDiscoverer.java | 4 +++- .../smack/serverless/{ => service}/LLPresenceListener.java | 4 +++- .../smack/serverless/{ => service}/LLService.java | 5 ++++- .../{ => service}/LLServiceConnectionListener.java | 4 +++- .../smack/serverless/{ => service}/LLServiceListener.java | 3 ++- .../serverless/{ => service}/LLServiceStateListener.java | 2 +- .../{ => service/jmdns}/JmDNSPresenceDiscoverer.java | 4 +++- .../smack/serverless/{ => service/jmdns}/JmDNSService.java | 5 ++++- .../src/test/java/org/jivesoftware/smackx/MDNSListener.java | 2 +- .../src/test/java/org/jivesoftware/smackx/TestMDNS.java | 6 +++--- 12 files changed, 32 insertions(+), 13 deletions(-) rename smack-serverless/src/main/java/org/jivesoftware/smack/serverless/{ => service}/LLPresenceDiscoverer.java (97%) rename smack-serverless/src/main/java/org/jivesoftware/smack/serverless/{ => service}/LLPresenceListener.java (91%) rename smack-serverless/src/main/java/org/jivesoftware/smack/serverless/{ => service}/LLService.java (99%) rename smack-serverless/src/main/java/org/jivesoftware/smack/serverless/{ => service}/LLServiceConnectionListener.java (89%) rename smack-serverless/src/main/java/org/jivesoftware/smack/serverless/{ => service}/LLServiceListener.java (94%) rename smack-serverless/src/main/java/org/jivesoftware/smack/serverless/{ => service}/LLServiceStateListener.java (96%) rename smack-serverless/src/main/java/org/jivesoftware/smack/serverless/{ => service/jmdns}/JmDNSPresenceDiscoverer.java (96%) rename smack-serverless/src/main/java/org/jivesoftware/smack/serverless/{ => service/jmdns}/JmDNSService.java (97%) diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceDiscoveryManager.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceDiscoveryManager.java index 1459ae2845..eb92011104 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceDiscoveryManager.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceDiscoveryManager.java @@ -22,7 +22,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; - import org.jivesoftware.smack.AbstractConnectionListener; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; @@ -30,6 +29,10 @@ import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.serverless.service.LLPresenceListener; +import org.jivesoftware.smack.serverless.service.LLService; +import org.jivesoftware.smack.serverless.service.LLServiceConnectionListener; +import org.jivesoftware.smack.serverless.service.LLServiceStateListener; import org.jivesoftware.smackx.disco.NodeInformationProvider; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.caps.EntityCapsManager; diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java index 3218542779..571b33a7fd 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java @@ -25,6 +25,7 @@ import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.PlainStreamElement; import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.serverless.service.LLService; import org.jivesoftware.smack.tcp.XMPPTCPConnection; import org.jivesoftware.smack.tcp.XMPPTCPConnection.PacketReader; import org.jivesoftware.smack.tcp.XMPPTCPConnection.PacketWriter; diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceDiscoverer.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLPresenceDiscoverer.java similarity index 97% rename from smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceDiscoverer.java rename to smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLPresenceDiscoverer.java index d010059404..fd56a07a56 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceDiscoverer.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLPresenceDiscoverer.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.jivesoftware.smack.serverless; +package org.jivesoftware.smack.serverless.service; import java.util.Set; @@ -24,6 +24,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; +import org.jivesoftware.smack.serverless.LLPresence; + /** * Link-local presence discoverer. XEP-0174 describes how to use mDNS/DNS-SD. * This class in an abstract representation of the basic functionality of diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceListener.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLPresenceListener.java similarity index 91% rename from smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceListener.java rename to smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLPresenceListener.java index e6ee0b9946..69fce85e19 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresenceListener.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLPresenceListener.java @@ -15,7 +15,9 @@ * limitations under the License. */ -package org.jivesoftware.smack.serverless; +package org.jivesoftware.smack.serverless.service; + +import org.jivesoftware.smack.serverless.LLPresence; /** diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLService.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java similarity index 99% rename from smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLService.java rename to smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java index e580b9095a..ac4d3e81c2 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLService.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.jivesoftware.smack.serverless; +package org.jivesoftware.smack.serverless.service; import org.jivesoftware.smack.AbstractConnectionListener; @@ -38,6 +38,9 @@ import org.jivesoftware.smack.filter.PacketIDFilter; import org.jivesoftware.smack.filter.IQTypeFilter; import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.serverless.LLConnectionConfiguration; +import org.jivesoftware.smack.serverless.LLPresence; +import org.jivesoftware.smack.serverless.XMPPLLConnection; import java.net.ServerSocket; import java.net.Socket; diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceConnectionListener.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceConnectionListener.java similarity index 89% rename from smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceConnectionListener.java rename to smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceConnectionListener.java index de655155ac..9d13e48f68 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceConnectionListener.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceConnectionListener.java @@ -14,7 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack.serverless; +package org.jivesoftware.smack.serverless.service; + +import org.jivesoftware.smack.serverless.XMPPLLConnection; /** diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceListener.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceListener.java similarity index 94% rename from smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceListener.java rename to smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceListener.java index 653d526074..f8af3d077f 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceListener.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceListener.java @@ -14,7 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack.serverless; +package org.jivesoftware.smack.serverless.service; + /** diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceStateListener.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceStateListener.java similarity index 96% rename from smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceStateListener.java rename to smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceStateListener.java index 000290ae10..01c0a4f090 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceStateListener.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceStateListener.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack.serverless; +package org.jivesoftware.smack.serverless.service; import org.jivesoftware.smack.packet.Message; diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSPresenceDiscoverer.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSPresenceDiscoverer.java similarity index 96% rename from smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSPresenceDiscoverer.java rename to smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSPresenceDiscoverer.java index 5c0ae260c9..6ae338d530 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSPresenceDiscoverer.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSPresenceDiscoverer.java @@ -15,10 +15,12 @@ * limitations under the License. */ -package org.jivesoftware.smack.serverless; +package org.jivesoftware.smack.serverless.service.jmdns; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.serverless.LLPresence; +import org.jivesoftware.smack.serverless.service.LLPresenceDiscoverer; import org.jivesoftware.smack.util.Tuple; import javax.jmdns.JmDNS; diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSService.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSService.java similarity index 97% rename from smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSService.java rename to smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSService.java index 47a5b38a9b..9f0e21bed0 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/JmDNSService.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSService.java @@ -15,11 +15,14 @@ * limitations under the License. */ -package org.jivesoftware.smack.serverless; +package org.jivesoftware.smack.serverless.service.jmdns; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.serverless.LLPresence; +import org.jivesoftware.smack.serverless.service.LLPresenceDiscoverer; +import org.jivesoftware.smack.serverless.service.LLService; import org.jivesoftware.smack.util.Tuple; import javax.jmdns.JmDNS; diff --git a/smack-serverless/src/test/java/org/jivesoftware/smackx/MDNSListener.java b/smack-serverless/src/test/java/org/jivesoftware/smackx/MDNSListener.java index f1796fab1c..5da9a3ff27 100644 --- a/smack-serverless/src/test/java/org/jivesoftware/smackx/MDNSListener.java +++ b/smack-serverless/src/test/java/org/jivesoftware/smackx/MDNSListener.java @@ -18,7 +18,7 @@ package org.jivesoftware.smackx; import org.jivesoftware.smack.serverless.LLPresence; -import org.jivesoftware.smack.serverless.LLPresenceListener; +import org.jivesoftware.smack.serverless.service.LLPresenceListener; public class MDNSListener implements LLPresenceListener { diff --git a/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java b/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java index f4d25310bf..dd9da50c4b 100644 --- a/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java +++ b/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java @@ -23,12 +23,12 @@ import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.serverless.JmDNSService; import org.jivesoftware.smack.serverless.LLChat; import org.jivesoftware.smack.serverless.LLPresence; -import org.jivesoftware.smack.serverless.LLService; import org.jivesoftware.smack.serverless.LLServiceDiscoveryManager; -import org.jivesoftware.smack.serverless.LLServiceStateListener; +import org.jivesoftware.smack.serverless.service.LLService; +import org.jivesoftware.smack.serverless.service.LLServiceStateListener; +import org.jivesoftware.smack.serverless.service.jmdns.JmDNSService; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import java.io.BufferedReader; From a0d2de1bdaa8ba2763f07028273a274da9eaa252 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 2 Dec 2014 10:44:36 +0100 Subject: [PATCH 3/8] Make it compile by commenting code --- .../serverless/LLConnectionConfiguration.java | 24 +- .../serverless/LLServiceDiscoveryManager.java | 96 +++--- .../smack/serverless/XMPPLLConnection.java | 302 +++++++++--------- .../smack/serverless/service/LLService.java | 148 ++++----- .../jmdns/JmDNSPresenceDiscoverer.java | 4 +- .../org/jivesoftware/smackx/TestMDNS.java | 93 +++--- 6 files changed, 331 insertions(+), 336 deletions(-) diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLConnectionConfiguration.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLConnectionConfiguration.java index d24287717d..60b71cb6b4 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLConnectionConfiguration.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLConnectionConfiguration.java @@ -45,11 +45,11 @@ public class LLConnectionConfiguration extends ConnectionConfiguration implement * @param local LLPresence for the local user * @param remote LLPresence for the remote user */ - public LLConnectionConfiguration(LLPresence local, LLPresence remote) { - super(remote.getServiceName()); - this.localPresence = local; - this.remotePresence = remote; - } +// public LLConnectionConfiguration(LLPresence local, LLPresence remote) { +//// super(remote.getServiceName()); +// this.localPresence = local; +// this.remotePresence = remote; +// } /** * Instantiate a link-local configuration when the connection is acting as @@ -64,13 +64,13 @@ public LLConnectionConfiguration(LLPresence local, Socket remoteSocket) { this.socket = remoteSocket; } - @Override - public void setServiceName(String serviceName) { - // ConnectionConfiguration#setServiceName extracts the domain from the serviceName - // e.g "david@guardian" -> "guardian" - // This is not the behavior we want for XEP-0174 clients - this.serviceName = serviceName; - } +// @Override +// public void setServiceName(String serviceName) { +// // ConnectionConfiguration#setServiceName extracts the domain from the serviceName +// // e.g "david@guardian" -> "guardian" +// // This is not the behavior we want for XEP-0174 clients +// this.serviceName = serviceName; +// } /** * Tells if the connection is the initiating one. diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceDiscoveryManager.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceDiscoveryManager.java index eb92011104..4cabc04af0 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceDiscoveryManager.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLServiceDiscoveryManager.java @@ -70,13 +70,13 @@ public class LLServiceDiscoveryManager extends ServiceDiscoveryManager { Please let me know (dbro@dbro.pro) if you've any thoughts on this matter. */ static { - XMPPLLConnection.addLLConnectionListener(new AbstractConnectionListener() { - - @Override - public void connected(XMPPLLConnection connection) { - addLLServiceDiscoveryManager(getInstanceFor(connection)); - } - }); +// XMPPLLConnection.addLLConnectionListener(new AbstractConnectionListener() { +// +// @Override +// public void connected(XMPPLLConnection connection) { +// addLLServiceDiscoveryManager(getInstanceFor(connection)); +// } +// }); } protected LLServiceDiscoveryManager(LLService llservice, XMPPConnection connection) { @@ -218,9 +218,9 @@ public void addDiscoverInfoTo(DiscoverInfo response) { for (String feature : getFeatures()) { response.addFeature(feature); } - if (extendedInfo != null) { - response.addExtension(extendedInfo); - } +// if (extendedInfo != null) { +// response.addExtension(extendedInfo); +// } } /** @@ -236,9 +236,9 @@ public DiscoverInfo getOwnDiscoverInfo() { // Add discover info addDiscoverInfoTo(di); - for (String feature : features) { - di.addFeature(feature); - } +// for (String feature : features) { +// di.addFeature(feature); +// } return di; } @@ -281,7 +281,7 @@ private ServiceDiscoveryManager getInstance(String serviceName) throws SmackExce */ @Override public void setExtendedInfo(DataForm info) { - extendedInfo = info; +// extendedInfo = info; // set for already active connections for (XMPPLLConnection connection : service.getConnections()) @@ -299,7 +299,7 @@ public void setExtendedInfo(DataForm info) { */ @Override public void removeExtendedInfo() { - extendedInfo = null; +// extendedInfo = null; // remove for already active connections for (XMPPLLConnection connection : service.getConnections()) @@ -330,7 +330,7 @@ public DiscoverInfo discoverInfo(String serviceName, String node) throws XMPPExc try { result = service.getIQResponse(disco); } catch (XMPPException | IOException | SmackException e) { - throw new SmackException.NoResponseException(); +// throw new SmackException.NoResponseException(); } if (result == null) { throw new XMPPException.XMPPErrorException("No response from the server.", new XMPPError(XMPPError.Condition.remote_server_timeout)); @@ -366,7 +366,7 @@ public DiscoverItems discoverItems(String serviceName, String node) throws Smack try { result = service.getIQResponse(disco); } catch (XMPPException | IOException | SmackException e) { - throw new SmackException.NoResponseException(); +// throw new SmackException.NoResponseException(); } if (result == null) { throw new XMPPException.XMPPErrorException("No response from the server.", new XMPPError(XMPPError.Condition.remote_server_timeout)); @@ -440,16 +440,16 @@ public void removeFeature(String feature) { } - /** - * Returns true if the specified feature is registered in the ServiceDiscoveryManager. - * - * @param feature the feature to look for. - * @return a boolean indicating if the specified featured is registered or not. - */ - @Override - public boolean includesFeature(String feature) { - return features.contains(feature); - } +// /** +// * Returns true if the specified feature is registered in the ServiceDiscoveryManager. +// * +// * @param feature the feature to look for. +// * @return a boolean indicating if the specified featured is registered or not. +// */ +// @Override +// public boolean includesFeature(String feature) { +// return features.contains(feature); +// } /** * Returns true if the server supports publishing of items. A client may wish to publish items @@ -507,14 +507,14 @@ public void publishItems(String entityID, String node, DiscoverItems discoverIte } } - private String getEntityCapsVersion() { - if (capsManager != null) { - return capsManager.getCapsVersion(); - } - else { - return null; - } - } +// private String getEntityCapsVersion() { +// if (capsManager != null) { +// return capsManager.getCapsVersion(); +// } +// else { +// return null; +// } +// } /** @@ -531,19 +531,19 @@ public void connectionCreated(XMPPLLConnection connection) { // Set Entity Capabilities Manager manager.setEntityCapsManager(capsManager); - // Set extended info - manager.setExtendedInfo(extendedInfo); - - // Set node information providers - for (Map.Entry entry : - nodeInformationProviders.entrySet()) { - manager.setNodeInformationProvider(entry.getKey(), entry.getValue()); - } - - // add features - for (String feature : features) { - manager.addFeature(feature); - } +// // Set extended info +// manager.setExtendedInfo(extendedInfo); +// +// // Set node information providers +// for (Map.Entry entry : +// nodeInformationProviders.entrySet()) { +// manager.setNodeInformationProvider(entry.getKey(), entry.getValue()); +// } +// +// // add features +// for (String feature : features) { +// manager.addFeature(feature); +// } } } diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java index 571b33a7fd..9644ba66b4 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java @@ -26,9 +26,6 @@ import org.jivesoftware.smack.packet.PlainStreamElement; import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smack.serverless.service.LLService; -import org.jivesoftware.smack.tcp.XMPPTCPConnection; -import org.jivesoftware.smack.tcp.XMPPTCPConnection.PacketReader; -import org.jivesoftware.smack.tcp.XMPPTCPConnection.PacketWriter; import org.xmlpull.v1.XmlPullParser; import java.io.BufferedReader; @@ -138,15 +135,15 @@ public boolean isInitiator() { return initiator; } - /** - * Return the user name of the remote peer (service name). - * - * @return the remote hosts service name / username - */ - public String getUser() { - // username is the service name of the local presence - return localPresence.getServiceName(); - } +// /** +// * Return the user name of the remote peer (service name). +// * +// * @return the remote hosts service name / username +// */ +// public String getUser() { +// // username is the service name of the local presence +// return localPresence.getServiceName(); +// } /** * Sets the name of the service provided in the from the remote peer. @@ -154,7 +151,7 @@ public String getUser() { * @param serviceName the name of the service */ public void setServiceName(String serviceName) { - ((LLConnectionConfiguration)config).setServiceName(serviceName); +// ((LLConnectionConfiguration)config).setServiceName(serviceName); //((LLConnectionConfiguration)config).setServiceName(remotePresence.getServiceName()); //LLConnectionConfiguration llconfig = new LLConnectionConfiguration(localPresence, remotePresence); //llconfig.setServiceName("Test"); @@ -188,19 +185,22 @@ public void connect() throws IOException, SmackException, XMPPException.XMPPErro try { socket = new Socket(host, port); + } catch (Exception e) { + // TODO + throw new SmackException(e); } - catch (UnknownHostException uhe) { - String errorMessage = "Could not connect to " + host + ":" + port + "."; - throw new XMPPException.XMPPErrorException(errorMessage, new XMPPError( - XMPPError.Condition.remote_server_timeout, errorMessage), - uhe); - } - catch (IOException ioe) { - String errorMessage = "Error connecting to " + host + ":" - + port + "."; - throw new XMPPException.XMPPErrorException(errorMessage, new XMPPError( - XMPPError.Condition.remote_server_error, errorMessage), ioe); - } +// catch (UnknownHostException uhe) { +// String errorMessage = "Could not connect to " + host + ":" + port + "."; +// throw new XMPPException.XMPPErrorException(errorMessage, new XMPPError( +// XMPPError.Condition.remote_server_timeout, errorMessage), +// uhe); +// } +// catch (IOException ioe) { +// String errorMessage = "Error connecting to " + host + ":" +// + port + "."; +// throw new XMPPException.XMPPErrorException(errorMessage, new XMPPError( +// XMPPError.Condition.remote_server_error, errorMessage), ioe); +// } initConnection(); notifyLLListenersConnected(); @@ -215,13 +215,14 @@ public void streamInitiatingReceived() throws XMPPException { if (config.getServiceName() == null) { shutdown(); } else { - packetWriter = new LLPacketWriter(); +// packetWriter = new LLPacketWriter(); if (debugger != null) { if (debugger.getWriterListener() != null) { addPacketListener(debugger.getWriterListener(), null); } } - packetWriter.startup(); + // TODO +// packetWriter.startup(); notifyLLListenersConnected(); } } @@ -268,11 +269,11 @@ private void initConnection() throws IOException, XMPPException.XMPPErrorExcepti // Don't initialize packet writer until we know it's a valid connection // unless we are the initiator. If we are NOT the initializer, we instead // wait for a stream initiation before doing anything. - if (isInitiator()) - packetWriter = new LLPacketWriter(); +// if (isInitiator()) +// packetWriter = new LLPacketWriter(); - // Initialize packet reader - packetReader = new LLPacketReader(); +// // Initialize packet reader +// packetReader = new LLPacketReader(); // If debugging is enabled, we should start the thread that will listen for // all packets and then log them. @@ -287,13 +288,14 @@ private void initConnection() throws IOException, XMPPException.XMPPErrorExcepti // If we are the initiator start the packet writer. This will open a XMPP // stream to the server. If not, a packet writer will be started after // receiving an initial stream start tag. - if (isInitiator()) - packetWriter.startup(); + // TODO +// if (isInitiator()) +// packetWriter.startup(); // Start the packet reader. The startup() method will block until we // get an opening stream packet back from server. - packetReader.startup(); +// packetReader.startup(); } - catch (XMPPException.XMPPErrorException | IOException | SmackException ex) { + catch (XMPPException.XMPPErrorException ex) { // An exception occurred in setting up the connection. Make sure we shut down the // readers and writers and close the socket. @@ -306,14 +308,14 @@ private void initConnection() throws IOException, XMPPException.XMPPErrorExcepti private void shutdownPacketReadersAndWritersAndCloseSocket() { if (packetWriter != null) { try { - packetWriter.shutdown(); +// packetWriter.shutdown(); } catch (Throwable ignore) { /* ignore */ } packetWriter = null; } if (packetReader != null) { try { - packetReader.shutdown(); +// packetReader.shutdown(); } catch (Throwable ignore) { /* ignore */ } packetReader = null; @@ -351,11 +353,13 @@ private void initReaderAndWriter() throws XMPPException.XMPPErrorException { new OutputStreamWriter(socket.getOutputStream(), "UTF-8")); } catch (IOException ioe) { - throw new XMPPException.XMPPErrorException( - "XMPPError establishing connection with server.", - new XMPPError(XMPPError.Condition.remote_server_error, - "XMPPError establishing connection with server."), - ioe); + // TODO + throw new RuntimeException(ioe); +// throw new XMPPException.XMPPErrorException( +// "XMPPError establishing connection with server.", +// new XMPPError(XMPPError.Condition.remote_server_error, +// "XMPPError establishing connection with server."), +// ioe); } // If debugging is enabled, we open a window and write out all network traffic. @@ -365,10 +369,10 @@ private void initReaderAndWriter() throws XMPPException.XMPPErrorException { protected void shutdown() { connection = null; - if (packetReader != null) - packetReader.shutdown(); - if (packetWriter != null) - packetWriter.shutdown(); +// if (packetReader != null) +// packetReader.shutdown(); +// if (packetWriter != null) +// packetWriter.shutdown(); // Wait 150 ms for processes to clean-up, then shutdown. try { @@ -414,99 +418,99 @@ public void disconnect() { packetReader = null; } - protected class LLPacketReader extends PacketReader { - - private boolean mGotStreamOpenedStanza = false; - - LLPacketReader() throws SmackException { - } - - public synchronized void startup() throws IOException, SmackException { - readerThread.start(); - - try { - // Wait until either: - // - the remote peer's stream initialization stanza has been parsed - // - an exception is thrown while parsing - // - the timeout occurs - if (connection.isInitiator()) - wait(getPacketReplyTimeout()); - } - catch (InterruptedException ie) { - // Ignore. - ie.printStackTrace(); - } - if (connection.isInitiator() && !mGotStreamOpenedStanza) { - throwConnectionExceptionOrNoResponse(); - } - } - - @Override - protected void handleStreamOpened(XmlPullParser parser) throws Exception { - super.handleStreamOpened(parser); - - // if we are the initiator, this means stream has been initiated - // if we aren't the initiator, this means we have to respond with - // stream initiator. - if (connection.isInitiator()) { - mGotStreamOpenedStanza = true; - connection.connectionID = connection.getServiceName(); - //releaseConnectionIDLock(); - } - else { - // Check if service name is a known entity - // if it is, open the stream and keep it open - // otherwise open and immediately close it - if (connection.getServiceName() == null) { - System.err.println("No service name specified in stream initiation, canceling."); - shutdown(); - } else { - // Check if service name is known, if so - // we will continue the session - LLPresence presence = service.getPresenceByServiceName(connection.getServiceName()); - if (presence != null) { - connection.setRemotePresence(presence); - connectionID = connection.getServiceName(); - connection.streamInitiatingReceived(); - //releaseConnectionIDLock(); - } else { - System.err.println("Unknown service name '" + - connection.getServiceName() + - "' specified in stream initation, canceling."); - shutdown(); - } - } - } - } - } - - protected class LLPacketWriter extends PacketWriter { - - - @Override - protected void openStream() throws IOException { - // Unlike traditional XMPP Stream initiation, - // we must provide our XEP-0174 Service Name - // in a "from" attribute - StringBuilder stream = new StringBuilder(); - stream.append(""); - writer.write(stream.toString()); - writer.flush(); - } - } +// protected class LLPacketReader extends PacketReader { +// +// private boolean mGotStreamOpenedStanza = false; +// +// LLPacketReader() throws SmackException { +// } +// +// public synchronized void startup() throws IOException, SmackException { +// readerThread.start(); +// +// try { +// // Wait until either: +// // - the remote peer's stream initialization stanza has been parsed +// // - an exception is thrown while parsing +// // - the timeout occurs +// if (connection.isInitiator()) +// wait(getPacketReplyTimeout()); +// } +// catch (InterruptedException ie) { +// // Ignore. +// ie.printStackTrace(); +// } +// if (connection.isInitiator() && !mGotStreamOpenedStanza) { +// throwConnectionExceptionOrNoResponse(); +// } +// } +// +// @Override +// protected void handleStreamOpened(XmlPullParser parser) throws Exception { +// super.handleStreamOpened(parser); +// +// // if we are the initiator, this means stream has been initiated +// // if we aren't the initiator, this means we have to respond with +// // stream initiator. +// if (connection.isInitiator()) { +// mGotStreamOpenedStanza = true; +// connection.connectionID = connection.getServiceName(); +// //releaseConnectionIDLock(); +// } +// else { +// // Check if service name is a known entity +// // if it is, open the stream and keep it open +// // otherwise open and immediately close it +// if (connection.getServiceName() == null) { +// System.err.println("No service name specified in stream initiation, canceling."); +// shutdown(); +// } else { +// // Check if service name is known, if so +// // we will continue the session +// LLPresence presence = service.getPresenceByServiceName(connection.getServiceName()); +// if (presence != null) { +// connection.setRemotePresence(presence); +// connectionID = connection.getServiceName(); +// connection.streamInitiatingReceived(); +// //releaseConnectionIDLock(); +// } else { +// System.err.println("Unknown service name '" + +// connection.getServiceName() + +// "' specified in stream initation, canceling."); +// shutdown(); +// } +// } +// } +// } +// } +// +// protected class LLPacketWriter extends PacketWriter { +// +// +// @Override +// protected void openStream() throws IOException { +// // Unlike traditional XMPP Stream initiation, +// // we must provide our XEP-0174 Service Name +// // in a "from" attribute +// StringBuilder stream = new StringBuilder(); +// stream.append(""); +// writer.write(stream.toString()); +// writer.flush(); +// } +// } @Override public String getConnectionID() { @@ -514,12 +518,6 @@ public String getConnectionID() { return null; } - @Override - public boolean isAuthenticated() { - // TODO Auto-generated method stub - return false; - } - @Override public boolean isSecureConnection() { // TODO Auto-generated method stub @@ -552,17 +550,17 @@ protected void connectInternal() throws SmackException, IOException, } - @Override - public void login(String username, String password, String resource) - throws XMPPException, SmackException, IOException { - // TODO Auto-generated method stub - - } - @Override public void loginAnonymously() throws XMPPException, SmackException, IOException { // TODO Auto-generated method stub } + + @Override + protected void loginNonAnonymously() throws XMPPException, SmackException, + IOException { + // TODO Auto-generated method stub + + } } diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java index ac4d3e81c2..849d552ad6 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java @@ -159,44 +159,44 @@ protected LLService(LLPresence presence, LLPresenceDiscoverer discoverer) { presenceDiscoverer = discoverer; service = this; - XMPPLLConnection.addLLConnectionListener(new AbstractConnectionListener() { - - @Override - public void connected(XMPPConnection xmppConnection) { - if (! (xmppConnection instanceof XMPPLLConnection)) { - return; - } - XMPPLLConnection connection = (XMPPLLConnection) xmppConnection; - // We only care about this connection if we were the one - // creating it - if (isAssociatedConnection(connection)) { - if (connection.isInitiator()) { - addOutgoingConnection(connection); - } - else { - addIngoingConnection(connection); - } - - connection.addConnectionListener(new ConnectionActivityListener(connection)); - - // Notify listeners that a new connection associated with this - // service has been created. - notifyNewServiceConnection(connection); - - - // add other existing packet filters associated with this service - for (ListenerWrapper wrapper : listeners.values()) { - connection.addPacketListener(wrapper.getPacketListener(), - wrapper.getPacketFilter()); - } - - // add packet collectors - for (CollectorWrapper cw : collectorWrappers) { - cw.createPacketCollector(connection); - } - } - } - }); +// XMPPLLConnection.addLLConnectionListener(new AbstractConnectionListener() { +// +// @Override +// public void connected(XMPPConnection xmppConnection) { +// if (! (xmppConnection instanceof XMPPLLConnection)) { +// return; +// } +// XMPPLLConnection connection = (XMPPLLConnection) xmppConnection; +// // We only care about this connection if we were the one +// // creating it +// if (isAssociatedConnection(connection)) { +// if (connection.isInitiator()) { +// addOutgoingConnection(connection); +// } +// else { +// addIngoingConnection(connection); +// } +// +// connection.addConnectionListener(new ConnectionActivityListener(connection)); +// +// // Notify listeners that a new connection associated with this +// // service has been created. +// notifyNewServiceConnection(connection); +// +// +// // add other existing packet filters associated with this service +// for (ListenerWrapper wrapper : listeners.values()) { +// connection.addPacketListener(wrapper.getPacketListener(), +// wrapper.getPacketFilter()); +// } +// +// // add packet collectors +// for (CollectorWrapper cw : collectorWrappers) { +// cw.createPacketCollector(connection); +// } +// } +// } +// }); notifyServiceListeners(this); } @@ -267,7 +267,7 @@ public synchronized static LLService getServiceInstance() throws XMPPException { public void init() throws XMPPException { // allocate a new port for remote clients to connect to socket = bindRange(DEFAULT_MIN_PORT, DEFAULT_MAX_PORT); - presence.setPort(socket.getLocalPort()); +// presence.setPort(socket.getLocalPort()); // register service on the allocated port registerService(); @@ -299,23 +299,23 @@ public void run() { public void close() throws IOException { done = true; - // close incoming connections - for (XMPPLLConnection connection : ingoing.values()) { - try { - connection.shutdown(); - } catch (Exception e) { - // ignore - } - } +// // close incoming connections +// for (XMPPLLConnection connection : ingoing.values()) { +// try { +// connection.shutdown(); +// } catch (Exception e) { +// // ignore +// } +// } - // close outgoing connections - for (XMPPLLConnection connection : outgoing.values()) { - try { - connection.shutdown(); - } catch (Exception e) { - // ignore - } - } +// // close outgoing connections +// for (XMPPLLConnection connection : outgoing.values()) { +// try { +// connection.shutdown(); +// } catch (Exception e) { +// // ignore +// } +// } try { socket.close(); } catch (IOException ioe) { @@ -337,19 +337,19 @@ private void listenForConnections() throws XMPPException { LLConnectionConfiguration config = new LLConnectionConfiguration(presence, s); - XMPPLLConnection connection = new XMPPLLConnection(this, config); +// XMPPLLConnection connection = new XMPPLLConnection(this, config); // Associate the new connection with this service - addAssociatedConnection(connection); +// addAssociatedConnection(connection); // Spawn new thread to handle the connecting. // The reason for spawning a new thread is to let two remote clients - // be able to connect at the same time. - Thread connectionInitiatorThread = - new ConnectionInitiatorThread(connection); - connectionInitiatorThread.setName("Smack Link-local Connection Initiator"); - connectionInitiatorThread.setDaemon(true); - connectionInitiatorThread.start(); +// // be able to connect at the same time. +// Thread connectionInitiatorThread = +// new ConnectionInitiatorThread(connection); +// connectionInitiatorThread.setName("Smack Link-local Connection Initiator"); +// connectionInitiatorThread.setDaemon(true); +// connectionInitiatorThread.start(); } catch (SocketException se) { // If we are closing down, it's probably closed socket exception. @@ -693,9 +693,9 @@ public XMPPLLConnection getConnection(String serviceName) throws XMPPException.X new XMPPError(XMPPError.Condition.recipient_unavailable)); } - LLConnectionConfiguration config = - new LLConnectionConfiguration(presence, remotePresence); - connection = new XMPPLLConnection(this, config); +// LLConnectionConfiguration config = +// new LLConnectionConfiguration(presence, remotePresence); +// connection = new XMPPLLConnection(this, config); // Associate the new connection with this service addAssociatedConnection(connection); connection.connect(); @@ -784,7 +784,7 @@ public IQ getIQResponse(IQ request) throws XMPPException, IOException, SmackExce * @throws XMPPException if an error occurs */ public void updateLocalPresence(LLPresence presence) throws XMPPException { - this.presence.update(presence); +// this.presence.update(presence); if (initiated) { updateText(); @@ -860,13 +860,13 @@ private class ConnectionInitiatorThread extends Thread { } public void run() { - try { - connection.initListen(); - } - catch (XMPPException | SmackException | IOException e) { - // ignore, since its an incoming connection - // there is nothing to save - } +// try { +// connection.initListen(); +// } +// catch (XMPPException | SmackException | IOException e) { +// // ignore, since its an incoming connection +// // there is nothing to save +// } } } diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSPresenceDiscoverer.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSPresenceDiscoverer.java index 6ae338d530..b66f336a41 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSPresenceDiscoverer.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSPresenceDiscoverer.java @@ -43,8 +43,8 @@ class JmDNSPresenceDiscoverer extends LLPresenceDiscoverer { JmDNSPresenceDiscoverer() throws XMPPException { jmdns = JmDNSService.jmdns; - if (jmdns == null) - throw new XMPPException.XMPPErrorException(new XMPPError(XMPPError.Condition.undefined_condition, "Failed to fully initiate mDNS daemon.")); +// if (jmdns == null) +// throw new XMPPException.XMPPErrorException(new XMPPError(XMPPError.Condition.undefined_condition, "Failed to fully initiate mDNS daemon.")); jmdns.addServiceListener(JmDNSService.SERVICE_TYPE, new PresenceServiceListener()); } diff --git a/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java b/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java index dd9da50c4b..4993c8ec2e 100644 --- a/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java +++ b/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java @@ -17,13 +17,10 @@ package org.jivesoftware.smackx; -import org.jivesoftware.smack.ChatManagerListener; -import org.jivesoftware.smack.MessageListener; import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.serverless.LLChat; import org.jivesoftware.smack.serverless.LLPresence; import org.jivesoftware.smack.serverless.LLServiceDiscoveryManager; import org.jivesoftware.smack.serverless.service.LLService; @@ -115,12 +112,12 @@ public void unknownOriginMessage(Message m) { // } System.out.println("Adding three default features to service discovery manager..."); - LLServiceDiscoveryManager.addDefaultFeature("http://www.jabber.org/extensions/lalal"); - LLServiceDiscoveryManager.addDefaultFeature("http://www.jabber.org/extenions/thetetteet"); - LLServiceDiscoveryManager.addDefaultFeature("urn:xmpp:hejhoppextension"); +// LLServiceDiscoveryManager.addDefaultFeature("http://www.jabber.org/extensions/lalal"); +// LLServiceDiscoveryManager.addDefaultFeature("http://www.jabber.org/extenions/thetetteet"); +// LLServiceDiscoveryManager.addDefaultFeature("urn:xmpp:hejhoppextension"); // Start listen for Link-local chats - service.addLLChatListener(new MyChatListener()); +// service.addLLChatListener(new MyChatListener()); // Add hook for doing a clean shut down Runtime.getRuntime().addShutdownHook(new CloseDownService(service)); @@ -148,14 +145,14 @@ else if ("msg".equals(line)) { String user = stdIn.readLine(); System.out.print("Enter message: "); String message = stdIn.readLine(); - LLChat chat = service.getChat(user); - chat.sendMessage(message); +// LLChat chat = service.getChat(user); +// chat.sendMessage(message); System.out.println("Message sent."); } else if ("addfeature".equals(line)) { System.out.print("Enter new feature: "); String feature = stdIn.readLine(); - LLServiceDiscoveryManager.addDefaultFeature(feature); +// LLServiceDiscoveryManager.addDefaultFeature(feature); } else if ("disco".equals(line)) { System.out.print("Enter user service name e.g (dave@service): "); @@ -226,42 +223,42 @@ public void run () { } } - private class MyChatListener implements ChatManagerListener { - public MyChatListener() {} - - @Override - public void chatCreated(LLChat chat, boolean createdLocally) { - System.out.println("Discovered new chat being created."); - chat.addMessageListener(new MyMessageListener(chat)); - } - } - - private class MyMessageListener implements MessageListener { - LLChat chat; - - MyMessageListener(LLChat chat) { - this.chat = chat; - } - - @Override - public void processMessage(LLChat chat, Message message) { - try { - if (message.getBody().equals("ping")) { - chat.sendMessage("pong"); - System.out.println("### received a ping, replied with pong."); - } - else if (message.getBody().equals("spam")) { - service.spam(); - } - else { - System.out.println("### <" + chat.getParticipant() + - "> " + message.getBody()); - } - } - catch (SmackException | XMPPException xe) { - System.out.println("Caught XMPPException in message listener: " + xe); - xe.printStackTrace(); - } - } - } +// private class MyChatListener implements ChatManagerListener { +// public MyChatListener() {} +// +// @Override +// public void chatCreated(LLChat chat, boolean createdLocally) { +// System.out.println("Discovered new chat being created."); +// chat.addMessageListener(new MyMessageListener(chat)); +// } +// } +// +// private class MyMessageListener implements MessageListener { +// LLChat chat; +// +// MyMessageListener(LLChat chat) { +// this.chat = chat; +// } +// +// @Override +// public void processMessage(LLChat chat, Message message) { +// try { +// if (message.getBody().equals("ping")) { +// chat.sendMessage("pong"); +// System.out.println("### received a ping, replied with pong."); +// } +// else if (message.getBody().equals("spam")) { +// service.spam(); +// } +// else { +// System.out.println("### <" + chat.getParticipant() + +// "> " + message.getBody()); +// } +// } +// catch (SmackException | XMPPException xe) { +// System.out.println("Caught XMPPException in message listener: " + xe); +// xe.printStackTrace(); +// } +// } +// } } From 626f592da263f025c325aa82b13bfadfad254d49 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 2 Dec 2014 17:45:09 +0100 Subject: [PATCH 4/8] Add LLStreamOpen --- .../jivesoftware/smack/packet/StreamOpen.java | 8 +++- .../smack/serverless/packet/LLStreamOpen.java | 39 +++++++++++++++++++ .../smack/serverless/service/LLService.java | 1 + 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/packet/LLStreamOpen.java diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/StreamOpen.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/StreamOpen.java index 62ef580a38..a28cbdc8d4 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/StreamOpen.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/StreamOpen.java @@ -46,12 +46,16 @@ public String getElementName() { @Override public XmlStringBuilder toXML() { + XmlStringBuilder xml = getBasicStreamOpen(); + xml.rightAngleBracket(); + return xml; + } + + protected final XmlStringBuilder getBasicStreamOpen() { XmlStringBuilder xml = new XmlStringBuilder(this); xml.attribute("to", service); xml.attribute("xmlns:stream", "http://etherx.jabber.org/streams"); xml.attribute("version", VERSION); - xml.rightAngleBracket(); return xml; } - } diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/packet/LLStreamOpen.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/packet/LLStreamOpen.java new file mode 100644 index 0000000000..ed85251a25 --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/packet/LLStreamOpen.java @@ -0,0 +1,39 @@ +/** + * + * Copyright © 2014 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.serverless.packet; + +import org.jivesoftware.smack.packet.StreamOpen; +import org.jivesoftware.smack.util.XmlStringBuilder; + +public class LLStreamOpen extends StreamOpen { + + private final String fromService; + + public LLStreamOpen(String toService, String fromService) { + super(toService); + this.fromService = fromService; + } + + @Override + public XmlStringBuilder toXML() { + XmlStringBuilder xml = getBasicStreamOpen(); + xml.attribute("from", fromService); + xml.rightAngleBracket(); + return xml; + } + +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java index 849d552ad6..c8f0931693 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java @@ -373,6 +373,7 @@ private void listenForConnections() throws XMPPException { * @throws XMPPException if binding failed on all allowed ports. */ private static ServerSocket bindRange(int min, int max) throws XMPPException { + // TODO this method exists also for the local socks5 proxy code and should be factored out into a util int port = 0; for (int try_port = min; try_port <= max; try_port++) { try { From 4ade6fb17d41c252723f16cf948394b45cd40ddf Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 2 Dec 2014 17:59:28 +0100 Subject: [PATCH 5/8] Update LLPresence --- .../smack/serverless/LLPresence.java | 134 +++++++++--------- 1 file changed, 70 insertions(+), 64 deletions(-) diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresence.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresence.java index 02d22873cd..0771e93816 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresence.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLPresence.java @@ -17,19 +17,15 @@ package org.jivesoftware.smack.serverless; - -import org.jivesoftware.smack.util.Tuple; - -import java.util.List; -import java.util.LinkedList; +import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * Class for describing a Link-local presence information according to XEP-0174. * XEP-0174 describes how to represent XMPP presences using mDNS/DNS-SD. * The presence information is stored as TXT fields; example from the documentation * follows: + *

        *        juliet IN TXT "txtvers=1"
        *        juliet IN TXT "1st=Juliet"
        *        juliet IN TXT "email=juliet@capulet.lit"
      @@ -44,21 +40,28 @@
        *        juliet IN TXT "status=avail"
        *        juliet IN TXT "vc=CA!"
        *        juliet IN TXT "ver=66/0NaeaBKkwk85efJTGmU47vXI="
      + * 
      */ public class LLPresence { // Service info, gathered from the TXT fields private String firstName, lastName, email, msg, nick, jid; // caps version private String hash, ver, node; - // XEP-0174 specifies that if status is not specified it is equal to "avail". + + /** + * XEP-0174 specifies that if status is not specified it is equal to "avail". + */ private Mode status = Mode.avail; - // The unknown - private Map rest = - new ConcurrentHashMap(); + /** + * The unknown + */ + private final Map rest = new HashMap<>(); public static enum Mode { - avail, away, dnd + avail, + away, + dnd } // Host details @@ -77,67 +80,69 @@ public LLPresence(String serviceName, String host, int port) { } public LLPresence(String serviceName, String host, int port, - List> records) { + Map records) { this(serviceName, host, port); - // Parse the tuple list (originating from the TXT fields) and put them + // Parse the map (originating from the TXT fields) and put them // in variables - for (Tuple t : records) { - if (t.a.equals("1st")) - setFirstName(t.b); - else if (t.a.equals("last")) - setLastName(t.b); - else if (t.a.equals("email")) - setEMail(t.b); - else if (t.a.equals("jid")) - setJID(t.b); - else if (t.a.equals("nick")) - setNick(t.b); - else if (t.a.equals("hash")) - setHash(t.b); - else if (t.a.equals("node")) - setNode(t.b); - else if (t.a.equals("ver")) - setVer(t.b); - else if (t.a.equals("status")) { - try { - setStatus(Mode.valueOf(t.b)); - } - catch (IllegalArgumentException iae) { - System.err.println("Found invalid presence status (" + - t.b + ") in TXT entry."); - } - } - else if (t.a.equals("msg")) - setMsg(t.b); - else { - // Unknown key - if (!rest.containsKey(t.a)) - rest.put(t.a, t.b); + for (Map.Entry entry : records.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + switch (key) { + case "1st": + setFirstName(value); + break; + case "last": + setLastName(value); + break; + case "email": + setEMail(value); + break; + case "jid": + setJID(value); + break; + case "nick": + setNick(value); + break; + case "hash": + setHash(value); + break; + case "node": + setNode(value); + break; + case "ver": + setVer(value); + break; + case "status": + setStatus(Mode.valueOf(value)); + break; + case "msg": + setMsg(value); + break; + default: + rest.put(key, value); } } } - public List> toList() { - LinkedList> list = new LinkedList<>(); - list.add(new Tuple<>("txtvers", "1")); - list.add(new Tuple<>("1st", firstName)); - list.add(new Tuple<>("last", lastName)); - list.add(new Tuple<>("email", email)); - list.add(new Tuple<>("jid", jid)); - list.add(new Tuple<>("nick", nick)); - list.add(new Tuple<>("status", status.toString())); - list.add(new Tuple<>("msg", msg)); - list.add(new Tuple<>("hash", hash)); - list.add(new Tuple<>("node", node)); - list.add(new Tuple<>("ver", ver)); - list.add(new Tuple<>("port.p2ppj", Integer.toString(port))); - - for (Map.Entry e : rest.entrySet()) { - list.add(new Tuple<>(e.getKey(), e.getValue())); - } + public Map toMap() { + Map map = new HashMap<>(rest.size() + 20); + map.put("txtvers", "1"); + map.put("1st", firstName); + map.put("last", lastName); + map.put("email", email); + map.put("jid", jid); + map.put("nick", nick); + map.put("status", status.toString()); + map.put("msg", msg); + map.put("hash", hash); + map.put("node", node); + map.put("ver", ver); + map.put("port.p2ppj", Integer.toString(port)); + + map.putAll(rest); - return list; + return map; } /** @@ -265,6 +270,7 @@ public void putValue(String key, String value) { rest.put(key, value); } + // TODO check where equals/hashcode are used public boolean equals(Object o) { if (o instanceof LLPresence) { LLPresence p = (LLPresence)o; From 5bd75ebed59e2365a61d52e8b9055ec5131e6cee Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 2 Dec 2014 18:00:57 +0100 Subject: [PATCH 6/8] Remove whitespace line --- .../java/org/jivesoftware/smack/ConnectionConfiguration.java | 1 - 1 file changed, 1 deletion(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java index 62b9cf339d..039f6fcb7f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -50,7 +50,6 @@ public abstract class ConnectionConfiguration { protected final String host; protected final int port; - private final String keystorePath; private final String keystoreType; private final String pkcs11Library; From 34e13e595d14ed9e5388ea6214177f6368684872 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 2 Dec 2014 18:13:23 +0100 Subject: [PATCH 7/8] Remove Tuple --- .../org/jivesoftware/smack/util/Tuple.java | 27 ------------------- .../jmdns/JmDNSPresenceDiscoverer.java | 19 ++++++------- .../service/jmdns/JmDNSService.java | 20 ++------------ 3 files changed, 12 insertions(+), 54 deletions(-) delete mode 100644 smack-core/src/main/java/org/jivesoftware/smack/util/Tuple.java diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/Tuple.java b/smack-core/src/main/java/org/jivesoftware/smack/util/Tuple.java deleted file mode 100644 index cbb8d97aec..0000000000 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/Tuple.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * - * Copyright 2003-2014 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smack.util; - -public class Tuple { - public A a; - public B b; - - public Tuple(A a, B b) { - this.a = a; - this.b = b; - } -} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSPresenceDiscoverer.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSPresenceDiscoverer.java index b66f336a41..109443819f 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSPresenceDiscoverer.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSPresenceDiscoverer.java @@ -21,15 +21,16 @@ import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smack.serverless.LLPresence; import org.jivesoftware.smack.serverless.service.LLPresenceDiscoverer; -import org.jivesoftware.smack.util.Tuple; import javax.jmdns.JmDNS; import javax.jmdns.ServiceEvent; import javax.jmdns.ServiceListener; import java.io.UnsupportedEncodingException; +import java.util.HashMap; import java.util.List; import java.util.LinkedList; +import java.util.Map; /** @@ -80,20 +81,20 @@ private static List TXTToList(byte[] bytes) { /** * Convert a TXT field list bundled with a '_presence._tcp' service to a - * String,String tuple. The TXT field list looks as following: - * "key=value" which is converted into the tuple (key, value). + * Map of Strings to Strings. The TXT field list looks as following: + * "key=value" which is converted into the a map of (key, value). * * @param strings the TXT fields. - * @return a list of key,value tuples. + * @return a map from key to value. */ - private static List> TXTListToXMPPRecords(List strings) { - // records :: [(String, String)] - List> records = new LinkedList>(); + private static Map TXTListToXMPPRecords(List strings) { + Map records = new HashMap<>(strings.size()); for (String s : strings) { String[] record = s.split("=", 2); // check if valid - if (record.length == 2) - records.add(new Tuple(record[0], record[1])); + if (record.length == 2) { + records.put(record[0], record[1]); + } } return records; } diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSService.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSService.java index 9f0e21bed0..66476904e2 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSService.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSService.java @@ -23,7 +23,6 @@ import org.jivesoftware.smack.serverless.LLPresence; import org.jivesoftware.smack.serverless.service.LLPresenceDiscoverer; import org.jivesoftware.smack.serverless.service.LLService; -import org.jivesoftware.smack.util.Tuple; import javax.jmdns.JmDNS; import javax.jmdns.ServiceEvent; @@ -33,7 +32,6 @@ import java.net.InetAddress; import java.io.IOException; -import java.util.Hashtable; import java.util.Map; /** @@ -104,29 +102,15 @@ private static void initJmDNS(InetAddress addr) throws XMPPException { } protected void updateText() { - Hashtable ht = new Hashtable(); - - for (Tuple t : presence.toList()) { - if (t.a != null && t.b != null) { - ht.put(t.a, t.b); - } - } - - serviceInfo.setText(ht); + serviceInfo.setText(presence.toMap()); } /** * Register the DNS-SD service with the daemon. */ protected void registerService() throws XMPPException { - Hashtable ht = new Hashtable(); - - for (Tuple t : presence.toList()) { - if (t.a != null && t.b != null) - ht.put(t.a, t.b); - } serviceInfo = ServiceInfo.create(SERVICE_TYPE, - presence.getServiceName(), presence.getPort(), 0, 0, ht); + presence.getServiceName(), presence.getPort(), 0, 0, presence.toMap()); jmdns.addServiceListener(SERVICE_TYPE, this); try { String originalServiceName = serviceInfo.getName(); From c03fb51ae88ff500a243e45256d08a4287fdfa31 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 3 Dec 2014 13:02:46 +0100 Subject: [PATCH 8/8] Add LLEntityCapsManager --- .../smackx/caps/EntityCapsManager.java | 7 +++- .../smack/serverless/LLEntityCapsManager.java | 35 +++++++++++++++++++ .../serverless/{service => }/LLService.java | 10 +++--- .../smack/serverless/XMPPLLConnection.java | 1 - .../serverless/service/LLServiceListener.java | 2 ++ .../service/jmdns/JmDNSService.java | 2 +- .../org/jivesoftware/smackx/TestMDNS.java | 26 ++++---------- 7 files changed, 57 insertions(+), 26 deletions(-) create mode 100644 smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLEntityCapsManager.java rename smack-serverless/src/main/java/org/jivesoftware/smack/serverless/{service => }/LLService.java (98%) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java index e6fd2e9aa2..a012f6c59d 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java @@ -265,7 +265,7 @@ public static void clearMemoryCache() { CAPS_CACHE.clear(); } - private static void addCapsExtensionInfo(String from, CapsExtension capsExtension) { + protected static void addCapsExtensionInfo(String from, CapsExtension capsExtension) { String capsExtensionHash = capsExtension.getHash(); String hashInUppercase = capsExtensionHash.toUpperCase(Locale.US); // SUPPORTED_HASHES uses the format of MessageDigest, which is uppercase, e.g. "SHA-1" instead of "sha-1" @@ -292,6 +292,11 @@ private static void addCapsExtensionInfo(String from, CapsExtension capsExtensio */ private String entityNode = DEFAULT_ENTITY_NODE; + protected EntityCapsManager() { + super(null); + throw new UnsupportedOperationException(); + } + private EntityCapsManager(XMPPConnection connection) { super(connection); this.sdm = ServiceDiscoveryManager.getInstanceFor(connection); diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLEntityCapsManager.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLEntityCapsManager.java new file mode 100644 index 0000000000..a64d52e67c --- /dev/null +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLEntityCapsManager.java @@ -0,0 +1,35 @@ +/** + * + * Copyright 2014 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.serverless; + +import org.jivesoftware.smackx.caps.EntityCapsManager; +import org.jivesoftware.smackx.caps.packet.CapsExtension; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; + +public class LLEntityCapsManager extends EntityCapsManager { + private LLEntityCapsManager() { + throw new UnsupportedOperationException(); + } + + static void addDiscoverInfo(String jid, DiscoverInfo discoverInfo) { + CapsExtension capsExtension = CapsExtension.from(discoverInfo); + if (capsExtension == null) { + return; + } + addCapsExtensionInfo(jid, capsExtension); + } +} diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLService.java similarity index 98% rename from smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java rename to smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLService.java index c8f0931693..53a43dcd64 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLService.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/LLService.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.jivesoftware.smack.serverless.service; +package org.jivesoftware.smack.serverless; import org.jivesoftware.smack.AbstractConnectionListener; @@ -38,9 +38,11 @@ import org.jivesoftware.smack.filter.PacketIDFilter; import org.jivesoftware.smack.filter.IQTypeFilter; import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smack.serverless.LLConnectionConfiguration; -import org.jivesoftware.smack.serverless.LLPresence; -import org.jivesoftware.smack.serverless.XMPPLLConnection; +import org.jivesoftware.smack.serverless.service.LLPresenceDiscoverer; +import org.jivesoftware.smack.serverless.service.LLPresenceListener; +import org.jivesoftware.smack.serverless.service.LLServiceConnectionListener; +import org.jivesoftware.smack.serverless.service.LLServiceListener; +import org.jivesoftware.smack.serverless.service.LLServiceStateListener; import java.net.ServerSocket; import java.net.Socket; diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java index 9644ba66b4..c2779585bf 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/XMPPLLConnection.java @@ -25,7 +25,6 @@ import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.PlainStreamElement; import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smack.serverless.service.LLService; import org.xmlpull.v1.XmlPullParser; import java.io.BufferedReader; diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceListener.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceListener.java index f8af3d077f..d9fed21bc2 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceListener.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/LLServiceListener.java @@ -16,6 +16,8 @@ */ package org.jivesoftware.smack.serverless.service; +import org.jivesoftware.smack.serverless.LLService; + /** diff --git a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSService.java b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSService.java index 66476904e2..777d27a675 100644 --- a/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSService.java +++ b/smack-serverless/src/main/java/org/jivesoftware/smack/serverless/service/jmdns/JmDNSService.java @@ -21,8 +21,8 @@ import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smack.serverless.LLPresence; +import org.jivesoftware.smack.serverless.LLService; import org.jivesoftware.smack.serverless.service.LLPresenceDiscoverer; -import org.jivesoftware.smack.serverless.service.LLService; import javax.jmdns.JmDNS; import javax.jmdns.ServiceEvent; diff --git a/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java b/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java index 4993c8ec2e..073ffd826d 100644 --- a/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java +++ b/smack-serverless/src/test/java/org/jivesoftware/smackx/TestMDNS.java @@ -18,15 +18,12 @@ package org.jivesoftware.smackx; import org.jivesoftware.smack.SmackConfiguration; -import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.serverless.LLPresence; -import org.jivesoftware.smack.serverless.LLServiceDiscoveryManager; -import org.jivesoftware.smack.serverless.service.LLService; +import org.jivesoftware.smack.serverless.LLService; import org.jivesoftware.smack.serverless.service.LLServiceStateListener; import org.jivesoftware.smack.serverless.service.jmdns.JmDNSService; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import java.io.BufferedReader; import java.io.IOException; @@ -154,12 +151,12 @@ else if ("addfeature".equals(line)) { String feature = stdIn.readLine(); // LLServiceDiscoveryManager.addDefaultFeature(feature); } - else if ("disco".equals(line)) { - System.out.print("Enter user service name e.g (dave@service): "); - String user = stdIn.readLine(); - DiscoverInfo info = LLServiceDiscoveryManager.getInstanceFor(service).discoverInfo(user); - System.out.println(" # Discovered: " + info.toXML()); - } +// else if ("disco".equals(line)) { +// System.out.print("Enter user service name e.g (dave@service): "); +// String user = stdIn.readLine(); +// DiscoverInfo info = LLServiceDiscoveryManager.getInstanceFor(service).discoverInfo(user); +// System.out.println(" # Discovered: " + info.toXML()); +// } else if ("status".equals(line)) { System.out.print("Enter new status: "); String status = stdIn.readLine(); @@ -181,15 +178,6 @@ else if ("status".equals(line)) { System.out.println("Caught IOException: " + ioe); ioe.printStackTrace(); done = true; - } catch (SmackException.NotConnectedException e) { - System.out.println("Caught NotConnectedException: " + e); - e.printStackTrace(); - } catch (SmackException.NoResponseException e) { - System.out.println("Caught NoResponseException: " + e); - e.printStackTrace(); - } catch (SmackException e) { - System.out.println("Caught SmackException: " + e); - e.printStackTrace(); } } System.exit(0);