001/** 002 * 003 * Copyright 2016 Fernando Ramirez 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.muclight; 018 019import java.lang.ref.WeakReference; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.WeakHashMap; 025 026import org.jivesoftware.smack.Manager; 027import org.jivesoftware.smack.SmackException.NoResponseException; 028import org.jivesoftware.smack.SmackException.NotConnectedException; 029import org.jivesoftware.smack.XMPPConnection; 030import org.jivesoftware.smack.XMPPException.XMPPErrorException; 031import org.jivesoftware.smack.filter.IQReplyFilter; 032import org.jivesoftware.smack.filter.StanzaFilter; 033import org.jivesoftware.smack.packet.IQ; 034 035import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 036import org.jivesoftware.smackx.disco.packet.DiscoverItems; 037import org.jivesoftware.smackx.muclight.element.MUCLightBlockingIQ; 038 039import org.jxmpp.jid.DomainBareJid; 040import org.jxmpp.jid.EntityBareJid; 041import org.jxmpp.jid.Jid; 042 043/** 044 * Multi-User Chat Light manager class. 045 * 046 * @author Fernando Ramirez 047 * 048 */ 049public final class MultiUserChatLightManager extends Manager { 050 051 private static final Map<XMPPConnection, MultiUserChatLightManager> INSTANCES = new WeakHashMap<XMPPConnection, MultiUserChatLightManager>(); 052 053 /** 054 * Get a instance of a MUC Light manager for the given connection. 055 * 056 * @param connection TODO javadoc me please 057 * @return a MUCLight manager. 058 */ 059 public static synchronized MultiUserChatLightManager getInstanceFor(XMPPConnection connection) { 060 MultiUserChatLightManager multiUserChatLightManager = INSTANCES.get(connection); 061 if (multiUserChatLightManager == null) { 062 multiUserChatLightManager = new MultiUserChatLightManager(connection); 063 INSTANCES.put(connection, multiUserChatLightManager); 064 } 065 return multiUserChatLightManager; 066 } 067 068 /** 069 * A Map of MUC Light JIDs to instances. We use weak references for the 070 * values in order to allow those instances to get garbage collected. 071 */ 072 private final Map<EntityBareJid, WeakReference<MultiUserChatLight>> multiUserChatLights = new HashMap<>(); 073 074 private MultiUserChatLightManager(XMPPConnection connection) { 075 super(connection); 076 } 077 078 /** 079 * Obtain the MUC Light. 080 * 081 * @param jid TODO javadoc me please 082 * @return the MUCLight. 083 */ 084 public synchronized MultiUserChatLight getMultiUserChatLight(EntityBareJid jid) { 085 WeakReference<MultiUserChatLight> weakRefMultiUserChat = multiUserChatLights.get(jid); 086 if (weakRefMultiUserChat == null) { 087 return createNewMucLightAndAddToMap(jid); 088 } 089 MultiUserChatLight multiUserChatLight = weakRefMultiUserChat.get(); 090 if (multiUserChatLight == null) { 091 return createNewMucLightAndAddToMap(jid); 092 } 093 return multiUserChatLight; 094 } 095 096 private MultiUserChatLight createNewMucLightAndAddToMap(EntityBareJid jid) { 097 MultiUserChatLight multiUserChatLight = new MultiUserChatLight(connection(), jid); 098 multiUserChatLights.put(jid, new WeakReference<>(multiUserChatLight)); 099 return multiUserChatLight; 100 } 101 102 /** 103 * Returns true if Multi-User Chat Light feature is supported by the server. 104 * 105 * @param mucLightService TODO javadoc me please 106 * @return true if Multi-User Chat Light feature is supported by the server. 107 * @throws NotConnectedException if the XMPP connection is not connected. 108 * @throws XMPPErrorException if there was an XMPP error returned. 109 * @throws NoResponseException if there was no response from the remote entity. 110 * @throws InterruptedException if the calling thread was interrupted. 111 */ 112 public boolean isFeatureSupported(DomainBareJid mucLightService) 113 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 114 return ServiceDiscoveryManager.getInstanceFor(connection()).discoverInfo(mucLightService) 115 .containsFeature(MultiUserChatLight.NAMESPACE); 116 } 117 118 /** 119 * Returns a List of the rooms the user occupies. 120 * 121 * @param mucLightService TODO javadoc me please 122 * @return a List of the rooms the user occupies. 123 * @throws XMPPErrorException if there was an XMPP error returned. 124 * @throws NoResponseException if there was no response from the remote entity. 125 * @throws NotConnectedException if the XMPP connection is not connected. 126 * @throws InterruptedException if the calling thread was interrupted. 127 */ 128 public List<Jid> getOccupiedRooms(DomainBareJid mucLightService) 129 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 130 DiscoverItems result = ServiceDiscoveryManager.getInstanceFor(connection()).discoverItems(mucLightService); 131 List<DiscoverItems.Item> items = result.getItems(); 132 List<Jid> answer = new ArrayList<>(items.size()); 133 134 for (DiscoverItems.Item item : items) { 135 Jid mucLight = item.getEntityID(); 136 answer.add(mucLight); 137 } 138 139 return answer; 140 } 141 142 /** 143 * Returns a collection with the XMPP addresses of the MUC Light services. 144 * 145 * @return a collection with the XMPP addresses of the MUC Light services. 146 * @throws XMPPErrorException if there was an XMPP error returned. 147 * @throws NoResponseException if there was no response from the remote entity. 148 * @throws NotConnectedException if the XMPP connection is not connected. 149 * @throws InterruptedException if the calling thread was interrupted. 150 */ 151 public List<DomainBareJid> getLocalServices() 152 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 153 ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection()); 154 return sdm.findServices(MultiUserChatLight.NAMESPACE, false, false); 155 } 156 157 /** 158 * Get users and rooms blocked. 159 * 160 * @param mucLightService TODO javadoc me please 161 * @return the list of users and rooms blocked 162 * @throws NoResponseException if there was no response from the remote entity. 163 * @throws XMPPErrorException if there was an XMPP error returned. 164 * @throws NotConnectedException if the XMPP connection is not connected. 165 * @throws InterruptedException if the calling thread was interrupted. 166 */ 167 public List<Jid> getUsersAndRoomsBlocked(DomainBareJid mucLightService) 168 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 169 MUCLightBlockingIQ muclIghtBlockingIQResult = getBlockingList(mucLightService); 170 171 List<Jid> jids = new ArrayList<>(); 172 if (muclIghtBlockingIQResult.getRooms() != null) { 173 jids.addAll(muclIghtBlockingIQResult.getRooms().keySet()); 174 } 175 176 if (muclIghtBlockingIQResult.getUsers() != null) { 177 jids.addAll(muclIghtBlockingIQResult.getUsers().keySet()); 178 } 179 180 return jids; 181 } 182 183 /** 184 * Get rooms blocked. 185 * 186 * @param mucLightService TODO javadoc me please 187 * @return the list of rooms blocked 188 * @throws NoResponseException if there was no response from the remote entity. 189 * @throws XMPPErrorException if there was an XMPP error returned. 190 * @throws NotConnectedException if the XMPP connection is not connected. 191 * @throws InterruptedException if the calling thread was interrupted. 192 */ 193 public List<Jid> getRoomsBlocked(DomainBareJid mucLightService) 194 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 195 MUCLightBlockingIQ mucLightBlockingIQResult = getBlockingList(mucLightService); 196 197 List<Jid> jids = new ArrayList<>(); 198 if (mucLightBlockingIQResult.getRooms() != null) { 199 jids.addAll(mucLightBlockingIQResult.getRooms().keySet()); 200 } 201 202 return jids; 203 } 204 205 /** 206 * Get users blocked. 207 * 208 * @param mucLightService TODO javadoc me please 209 * @return the list of users blocked 210 * @throws NoResponseException if there was no response from the remote entity. 211 * @throws XMPPErrorException if there was an XMPP error returned. 212 * @throws NotConnectedException if the XMPP connection is not connected. 213 * @throws InterruptedException if the calling thread was interrupted. 214 */ 215 public List<Jid> getUsersBlocked(DomainBareJid mucLightService) 216 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 217 MUCLightBlockingIQ mucLightBlockingIQResult = getBlockingList(mucLightService); 218 219 List<Jid> jids = new ArrayList<>(); 220 if (mucLightBlockingIQResult.getUsers() != null) { 221 jids.addAll(mucLightBlockingIQResult.getUsers().keySet()); 222 } 223 224 return jids; 225 } 226 227 private MUCLightBlockingIQ getBlockingList(DomainBareJid mucLightService) 228 throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException { 229 MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(null, null); 230 mucLightBlockingIQ.setType(IQ.Type.get); 231 mucLightBlockingIQ.setTo(mucLightService); 232 233 StanzaFilter responseFilter = new IQReplyFilter(mucLightBlockingIQ, connection()); 234 IQ responseIq = connection().createStanzaCollectorAndSend(responseFilter, mucLightBlockingIQ) 235 .nextResultOrThrow(); 236 MUCLightBlockingIQ mucLightBlockingIQResult = (MUCLightBlockingIQ) responseIq; 237 238 return mucLightBlockingIQResult; 239 } 240 241 /** 242 * Block a room. 243 * 244 * @param mucLightService TODO javadoc me please 245 * @param roomJid TODO javadoc me please 246 * @throws NoResponseException if there was no response from the remote entity. 247 * @throws XMPPErrorException if there was an XMPP error returned. 248 * @throws NotConnectedException if the XMPP connection is not connected. 249 * @throws InterruptedException if the calling thread was interrupted. 250 */ 251 public void blockRoom(DomainBareJid mucLightService, Jid roomJid) 252 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 253 HashMap<Jid, Boolean> rooms = new HashMap<>(); 254 rooms.put(roomJid, false); 255 sendBlockRooms(mucLightService, rooms); 256 } 257 258 /** 259 * Block rooms. 260 * 261 * @param mucLightService TODO javadoc me please 262 * @param roomsJids TODO javadoc me please 263 * @throws NoResponseException if there was no response from the remote entity. 264 * @throws XMPPErrorException if there was an XMPP error returned. 265 * @throws NotConnectedException if the XMPP connection is not connected. 266 * @throws InterruptedException if the calling thread was interrupted. 267 */ 268 public void blockRooms(DomainBareJid mucLightService, List<Jid> roomsJids) 269 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 270 HashMap<Jid, Boolean> rooms = new HashMap<>(); 271 for (Jid jid : roomsJids) { 272 rooms.put(jid, false); 273 } 274 sendBlockRooms(mucLightService, rooms); 275 } 276 277 private void sendBlockRooms(DomainBareJid mucLightService, Map<Jid, Boolean> rooms) 278 throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException { 279 MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(rooms, null); 280 mucLightBlockingIQ.setType(IQ.Type.set); 281 mucLightBlockingIQ.setTo(mucLightService); 282 connection().sendIqRequestAndWaitForResponse(mucLightBlockingIQ); 283 } 284 285 /** 286 * Block a user. 287 * 288 * @param mucLightService TODO javadoc me please 289 * @param userJid TODO javadoc me please 290 * @throws NoResponseException if there was no response from the remote entity. 291 * @throws XMPPErrorException if there was an XMPP error returned. 292 * @throws NotConnectedException if the XMPP connection is not connected. 293 * @throws InterruptedException if the calling thread was interrupted. 294 */ 295 public void blockUser(DomainBareJid mucLightService, Jid userJid) 296 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 297 HashMap<Jid, Boolean> users = new HashMap<>(); 298 users.put(userJid, false); 299 sendBlockUsers(mucLightService, users); 300 } 301 302 /** 303 * Block users. 304 * 305 * @param mucLightService TODO javadoc me please 306 * @param usersJids TODO javadoc me please 307 * @throws NoResponseException if there was no response from the remote entity. 308 * @throws XMPPErrorException if there was an XMPP error returned. 309 * @throws NotConnectedException if the XMPP connection is not connected. 310 * @throws InterruptedException if the calling thread was interrupted. 311 */ 312 public void blockUsers(DomainBareJid mucLightService, List<Jid> usersJids) 313 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 314 HashMap<Jid, Boolean> users = new HashMap<>(); 315 for (Jid jid : usersJids) { 316 users.put(jid, false); 317 } 318 sendBlockUsers(mucLightService, users); 319 } 320 321 private void sendBlockUsers(DomainBareJid mucLightService, Map<Jid, Boolean> users) 322 throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException { 323 MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(null, users); 324 mucLightBlockingIQ.setType(IQ.Type.set); 325 mucLightBlockingIQ.setTo(mucLightService); 326 connection().sendIqRequestAndWaitForResponse(mucLightBlockingIQ); 327 } 328 329 /** 330 * Unblock a room. 331 * 332 * @param mucLightService TODO javadoc me please 333 * @param roomJid TODO javadoc me please 334 * @throws NoResponseException if there was no response from the remote entity. 335 * @throws XMPPErrorException if there was an XMPP error returned. 336 * @throws NotConnectedException if the XMPP connection is not connected. 337 * @throws InterruptedException if the calling thread was interrupted. 338 */ 339 public void unblockRoom(DomainBareJid mucLightService, Jid roomJid) 340 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 341 HashMap<Jid, Boolean> rooms = new HashMap<>(); 342 rooms.put(roomJid, true); 343 sendUnblockRooms(mucLightService, rooms); 344 } 345 346 /** 347 * Unblock rooms. 348 * 349 * @param mucLightService TODO javadoc me please 350 * @param roomsJids TODO javadoc me please 351 * @throws NoResponseException if there was no response from the remote entity. 352 * @throws XMPPErrorException if there was an XMPP error returned. 353 * @throws NotConnectedException if the XMPP connection is not connected. 354 * @throws InterruptedException if the calling thread was interrupted. 355 */ 356 public void unblockRooms(DomainBareJid mucLightService, List<Jid> roomsJids) 357 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 358 HashMap<Jid, Boolean> rooms = new HashMap<>(); 359 for (Jid jid : roomsJids) { 360 rooms.put(jid, true); 361 } 362 sendUnblockRooms(mucLightService, rooms); 363 } 364 365 private void sendUnblockRooms(DomainBareJid mucLightService, Map<Jid, Boolean> rooms) 366 throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException { 367 MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(rooms, null); 368 mucLightBlockingIQ.setType(IQ.Type.set); 369 mucLightBlockingIQ.setTo(mucLightService); 370 connection().sendIqRequestAndWaitForResponse(mucLightBlockingIQ); 371 } 372 373 /** 374 * Unblock a user. 375 * 376 * @param mucLightService TODO javadoc me please 377 * @param userJid TODO javadoc me please 378 * @throws NoResponseException if there was no response from the remote entity. 379 * @throws XMPPErrorException if there was an XMPP error returned. 380 * @throws NotConnectedException if the XMPP connection is not connected. 381 * @throws InterruptedException if the calling thread was interrupted. 382 */ 383 public void unblockUser(DomainBareJid mucLightService, Jid userJid) 384 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 385 HashMap<Jid, Boolean> users = new HashMap<>(); 386 users.put(userJid, true); 387 sendUnblockUsers(mucLightService, users); 388 } 389 390 /** 391 * Unblock users. 392 * 393 * @param mucLightService TODO javadoc me please 394 * @param usersJids TODO javadoc me please 395 * @throws NoResponseException if there was no response from the remote entity. 396 * @throws XMPPErrorException if there was an XMPP error returned. 397 * @throws NotConnectedException if the XMPP connection is not connected. 398 * @throws InterruptedException if the calling thread was interrupted. 399 */ 400 public void unblockUsers(DomainBareJid mucLightService, List<Jid> usersJids) 401 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 402 HashMap<Jid, Boolean> users = new HashMap<>(); 403 for (Jid jid : usersJids) { 404 users.put(jid, true); 405 } 406 sendUnblockUsers(mucLightService, users); 407 } 408 409 private void sendUnblockUsers(DomainBareJid mucLightService, Map<Jid, Boolean> users) 410 throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException { 411 MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(null, users); 412 mucLightBlockingIQ.setType(IQ.Type.set); 413 mucLightBlockingIQ.setTo(mucLightService); 414 connection().sendIqRequestAndWaitForResponse(mucLightBlockingIQ); 415 } 416 417}