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.SmackConfiguration; 024import org.jivesoftware.smack.SmackException.NoResponseException; 025import org.jivesoftware.smack.SmackException.NotConnectedException; 026import org.jivesoftware.smack.ConnectionCreationListener; 027import org.jivesoftware.smack.XMPPConnection; 028import org.jivesoftware.smack.Manager; 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.XMPPError.Condition; 035import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 036import org.jivesoftware.smackx.iqversion.packet.Version; 037import org.jxmpp.jid.Jid; 038 039/** 040 * A Version Manager that automatically responds to version IQs with a predetermined reply. 041 * 042 * <p> 043 * The VersionManager takes care of handling incoming version request IQs, according to 044 * XEP-0092 (Software Version). You can configure the version reply for a given connection 045 * by running the following code: 046 * </p> 047 * 048 * <pre> 049 * Version MY_VERSION = new Version("My Little XMPP Application", "v1.23", "OS/2 32-bit"); 050 * VersionManager.getInstanceFor(mConnection).setVersion(MY_VERSION); 051 * </pre> 052 * 053 * @author Georg Lukas 054 */ 055public final class VersionManager extends Manager { 056 private static final Map<XMPPConnection, VersionManager> INSTANCES = new WeakHashMap<XMPPConnection, VersionManager>(); 057 058 private static Version defaultVersion; 059 060 private Version ourVersion = defaultVersion; 061 062 public static void setDefaultVersion(String name, String version) { 063 setDefaultVersion(name, version, null); 064 } 065 066 public static void setDefaultVersion(String name, String version, String os) { 067 defaultVersion = generateVersionFrom(name, version, os); 068 } 069 070 private static boolean autoAppendSmackVersion = true; 071 072 static { 073 XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { 074 @Override 075 public void connectionCreated(XMPPConnection connection) { 076 VersionManager.getInstanceFor(connection); 077 } 078 }); 079 } 080 081 private VersionManager(final XMPPConnection connection) { 082 super(connection); 083 084 ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); 085 sdm.addFeature(Version.NAMESPACE); 086 087 connection.registerIQRequestHandler(new AbstractIqRequestHandler(Version.ELEMENT, Version.NAMESPACE, IQ.Type.get, 088 Mode.async) { 089 @Override 090 public IQ handleIQRequest(IQ iqRequest) { 091 if (ourVersion == null) { 092 return IQ.createErrorResponse(iqRequest, Condition.not_acceptable); 093 } 094 095 return Version.createResultFor(iqRequest, ourVersion); 096 } 097 }); 098 } 099 100 public static synchronized VersionManager getInstanceFor(XMPPConnection connection) { 101 VersionManager versionManager = INSTANCES.get(connection); 102 103 if (versionManager == null) { 104 versionManager = new VersionManager(connection); 105 INSTANCES.put(connection, versionManager); 106 } 107 108 return versionManager; 109 } 110 111 public static void setAutoAppendSmackVersion(boolean autoAppendSmackVersion) { 112 VersionManager.autoAppendSmackVersion = autoAppendSmackVersion; 113 } 114 115 public void setVersion(String name, String version) { 116 setVersion(name, version, null); 117 } 118 119 public void setVersion(String name, String version, String os) { 120 ourVersion = generateVersionFrom(name, version, os); 121 } 122 123 public void unsetVersion() { 124 ourVersion = null; 125 } 126 127 public boolean isSupported(Jid jid) throws NoResponseException, XMPPErrorException, 128 NotConnectedException, InterruptedException { 129 return ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(jid, 130 Version.NAMESPACE); 131 } 132 133 /** 134 * Request version information from a given JID. 135 * 136 * @param jid 137 * @return the version information or {@code null} if not supported by JID 138 * @throws NoResponseException 139 * @throws XMPPErrorException 140 * @throws NotConnectedException 141 * @throws InterruptedException 142 */ 143 public Version getVersion(Jid jid) throws NoResponseException, XMPPErrorException, 144 NotConnectedException, InterruptedException { 145 if (!isSupported(jid)) { 146 return null; 147 } 148 return connection().createStanzaCollectorAndSend(new Version(jid)).nextResultOrThrow(); 149 } 150 151 private static Version generateVersionFrom(String name, String version, String os) { 152 if (autoAppendSmackVersion) { 153 name += " (Smack " + SmackConfiguration.getVersion() + ')'; 154 } 155 return new Version(name, version, os); 156 } 157}