001/** 002 * 003 * Copyright 2014 Georg Lukas. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.jivesoftware.smackx.iqversion; 019 020import java.util.Map; 021import java.util.WeakHashMap; 022 023import org.jivesoftware.smack.ConnectionCreationListener; 024import org.jivesoftware.smack.Manager; 025import org.jivesoftware.smack.Smack; 026import org.jivesoftware.smack.SmackException.NoResponseException; 027import org.jivesoftware.smack.SmackException.NotConnectedException; 028import org.jivesoftware.smack.XMPPConnection; 029import org.jivesoftware.smack.XMPPConnectionRegistry; 030import org.jivesoftware.smack.XMPPException.XMPPErrorException; 031import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler; 032import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode; 033import org.jivesoftware.smack.packet.IQ; 034import org.jivesoftware.smack.packet.StanzaError.Condition; 035 036import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 037import org.jivesoftware.smackx.iqversion.packet.Version; 038 039import org.jxmpp.jid.Jid; 040 041/** 042 * A Version Manager that automatically responds to version IQs with a predetermined reply. 043 * 044 * <p> 045 * The VersionManager takes care of handling incoming version request IQs, according to 046 * XEP-0092 (Software Version). You can configure the version reply for a given connection 047 * by running the following code: 048 * </p> 049 * 050 * <pre> 051 * Version MY_VERSION = new Version("My Little XMPP Application", "v1.23", "OS/2 32-bit"); 052 * VersionManager.getInstanceFor(mConnection).setVersion(MY_VERSION); 053 * </pre> 054 * 055 * @author Georg Lukas 056 */ 057public final class VersionManager extends Manager { 058 private static final Map<XMPPConnection, VersionManager> INSTANCES = new WeakHashMap<>(); 059 060 private static Version defaultVersion; 061 062 private Version ourVersion = defaultVersion; 063 064 public static void setDefaultVersion(String name, String version) { 065 setDefaultVersion(name, version, null); 066 } 067 068 public static void setDefaultVersion(String name, String version, String os) { 069 defaultVersion = generateVersionFrom(name, version, os); 070 } 071 072 private static boolean autoAppendSmackVersion = true; 073 074 static { 075 XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { 076 @Override 077 public void connectionCreated(XMPPConnection connection) { 078 VersionManager.getInstanceFor(connection); 079 } 080 }); 081 } 082 083 private VersionManager(final XMPPConnection connection) { 084 super(connection); 085 086 ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); 087 sdm.addFeature(Version.NAMESPACE); 088 089 connection.registerIQRequestHandler(new AbstractIqRequestHandler(Version.ELEMENT, Version.NAMESPACE, IQ.Type.get, 090 Mode.async) { 091 @Override 092 public IQ handleIQRequest(IQ iqRequest) { 093 if (ourVersion == null) { 094 return IQ.createErrorResponse(iqRequest, Condition.not_acceptable); 095 } 096 097 return Version.createResultFor(iqRequest, ourVersion); 098 } 099 }); 100 } 101 102 public static synchronized VersionManager getInstanceFor(XMPPConnection connection) { 103 VersionManager versionManager = INSTANCES.get(connection); 104 105 if (versionManager == null) { 106 versionManager = new VersionManager(connection); 107 INSTANCES.put(connection, versionManager); 108 } 109 110 return versionManager; 111 } 112 113 public static void setAutoAppendSmackVersion(boolean autoAppendSmackVersion) { 114 VersionManager.autoAppendSmackVersion = autoAppendSmackVersion; 115 } 116 117 public void setVersion(String name, String version) { 118 setVersion(name, version, null); 119 } 120 121 public void setVersion(String name, String version, String os) { 122 ourVersion = generateVersionFrom(name, version, os); 123 } 124 125 public void unsetVersion() { 126 ourVersion = null; 127 } 128 129 public boolean isSupported(Jid jid) throws NoResponseException, XMPPErrorException, 130 NotConnectedException, InterruptedException { 131 return ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(jid, 132 Version.NAMESPACE); 133 } 134 135 /** 136 * Request version information from a given JID. 137 * 138 * @param jid TODO javadoc me please 139 * @return the version information or {@code null} if not supported by JID 140 * @throws NoResponseException if there was no response from the remote entity. 141 * @throws XMPPErrorException if there was an XMPP error returned. 142 * @throws NotConnectedException if the XMPP connection is not connected. 143 * @throws InterruptedException if the calling thread was interrupted. 144 */ 145 public Version getVersion(Jid jid) throws NoResponseException, XMPPErrorException, 146 NotConnectedException, InterruptedException { 147 if (!isSupported(jid)) { 148 return null; 149 } 150 return connection().createStanzaCollectorAndSend(new Version(jid)).nextResultOrThrow(); 151 } 152 153 private static Version generateVersionFrom(String name, String version, String os) { 154 if (autoAppendSmackVersion) { 155 name += " (Smack " + Smack.getVersion() + ')'; 156 } 157 return new Version(name, version, os); 158 } 159}