001/** 002 * 003 * Copyright 2016-2017 Fernando Ramirez, Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.bob; 018 019import java.util.Collections; 020import java.util.Map; 021import java.util.Set; 022import java.util.WeakHashMap; 023import java.util.concurrent.ConcurrentHashMap; 024 025import org.jivesoftware.smack.ConnectionCreationListener; 026import org.jivesoftware.smack.Manager; 027import org.jivesoftware.smack.SmackException.NoResponseException; 028import org.jivesoftware.smack.SmackException.NotConnectedException; 029import org.jivesoftware.smack.SmackException.NotLoggedInException; 030import org.jivesoftware.smack.XMPPConnection; 031import org.jivesoftware.smack.XMPPConnectionRegistry; 032import org.jivesoftware.smack.XMPPException.XMPPErrorException; 033import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler; 034import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode; 035import org.jivesoftware.smack.packet.IQ; 036import org.jivesoftware.smack.packet.IQ.Type; 037import org.jivesoftware.smack.util.SHA1; 038 039import org.jivesoftware.smackx.bob.element.BoBIQ; 040import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 041 042import org.jxmpp.jid.Jid; 043import org.jxmpp.util.cache.LruCache; 044 045/** 046 * Bits of Binary manager class. 047 * 048 * @author Fernando Ramirez 049 * @author Florian Schmaus 050 * @see <a href="http://xmpp.org/extensions/xep-0231.html">XEP-0231: Bits of 051 * Binary</a> 052 */ 053public final class BoBManager extends Manager { 054 055 public static final String NAMESPACE = "urn:xmpp:bob"; 056 057 static { 058 XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { 059 @Override 060 public void connectionCreated(XMPPConnection connection) { 061 getInstanceFor(connection); 062 } 063 }); 064 } 065 066 private static final Map<XMPPConnection, BoBManager> INSTANCES = new WeakHashMap<>(); 067 068 /** 069 * Get the singleton instance of BoBManager. 070 * 071 * @param connection 072 * @return the instance of BoBManager 073 */ 074 public static synchronized BoBManager getInstanceFor(XMPPConnection connection) { 075 BoBManager bobManager = INSTANCES.get(connection); 076 if (bobManager == null) { 077 bobManager = new BoBManager(connection); 078 INSTANCES.put(connection, bobManager); 079 } 080 081 return bobManager; 082 } 083 084 private static final LruCache<BoBHash, BoBData> BOB_CACHE = new LruCache<>(128); 085 086 private final Map<BoBHash, BoBInfo> bobs = new ConcurrentHashMap<>(); 087 088 private BoBManager(XMPPConnection connection) { 089 super(connection); 090 ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); 091 serviceDiscoveryManager.addFeature(NAMESPACE); 092 093 connection.registerIQRequestHandler( 094 new AbstractIqRequestHandler(BoBIQ.ELEMENT, BoBIQ.NAMESPACE, Type.get, Mode.async) { 095 @Override 096 public IQ handleIQRequest(IQ iqRequest) { 097 BoBIQ bobIQRequest = (BoBIQ) iqRequest; 098 099 BoBInfo bobInfo = bobs.get(bobIQRequest.getBoBHash()); 100 if (bobInfo == null) { 101 // TODO return item-not-found 102 return null; 103 } 104 105 BoBData bobData = bobInfo.getData(); 106 BoBIQ responseBoBIQ = new BoBIQ(bobIQRequest.getBoBHash(), bobData); 107 responseBoBIQ.setType(Type.result); 108 responseBoBIQ.setTo(bobIQRequest.getFrom()); 109 return responseBoBIQ; 110 } 111 }); 112 } 113 114 /** 115 * Returns true if Bits of Binary is supported by the server. 116 * 117 * @return true if Bits of Binary is supported by the server. 118 * @throws NoResponseException 119 * @throws XMPPErrorException 120 * @throws NotConnectedException 121 * @throws InterruptedException 122 */ 123 public boolean isSupportedByServer() 124 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 125 return ServiceDiscoveryManager.getInstanceFor(connection()).serverSupportsFeature(NAMESPACE); 126 } 127 128 /** 129 * Request BoB data. 130 * 131 * @param to 132 * @param bobHash 133 * @return the BoB data 134 * @throws NotLoggedInException 135 * @throws NoResponseException 136 * @throws XMPPErrorException 137 * @throws NotConnectedException 138 * @throws InterruptedException 139 */ 140 public BoBData requestBoB(Jid to, BoBHash bobHash) throws NotLoggedInException, NoResponseException, 141 XMPPErrorException, NotConnectedException, InterruptedException { 142 BoBData bobData = BOB_CACHE.lookup(bobHash); 143 if (bobData != null) { 144 return bobData; 145 } 146 147 BoBIQ requestBoBIQ = new BoBIQ(bobHash); 148 requestBoBIQ.setType(Type.get); 149 requestBoBIQ.setTo(to); 150 151 XMPPConnection connection = getAuthenticatedConnectionOrThrow(); 152 BoBIQ responseBoBIQ = connection.createStanzaCollectorAndSend(requestBoBIQ).nextResultOrThrow(); 153 154 bobData = responseBoBIQ.getBoBData(); 155 BOB_CACHE.put(bobHash, bobData); 156 157 return bobData; 158 } 159 160 public BoBInfo addBoB(BoBData bobData) { 161 // We only support SHA-1 for now. 162 BoBHash bobHash = new BoBHash(SHA1.hex(bobData.getContent()), "sha1"); 163 164 Set<BoBHash> bobHashes = Collections.singleton(bobHash); 165 bobHashes = Collections.unmodifiableSet(bobHashes); 166 167 BoBInfo bobInfo = new BoBInfo(bobHashes, bobData); 168 169 bobs.put(bobHash, bobInfo); 170 171 return bobInfo; 172 } 173 174 public BoBInfo removeBoB(BoBHash bobHash) { 175 BoBInfo bobInfo = bobs.remove(bobHash); 176 if (bobInfo == null) { 177 return null; 178 } 179 for (BoBHash otherBobHash : bobInfo.getHashes()) { 180 bobs.remove(otherBobHash); 181 } 182 return bobInfo; 183 } 184}