/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * 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 net.java.sip.communicator.impl.muc; import java.util.*; import net.java.sip.communicator.service.muc.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; import org.jitsi.service.configuration.*; import org.osgi.framework.*; /** * The <tt>ChatRoomsList</tt> is the list containing all chat rooms. * * @author Yana Stamcheva * @author Hristo Terezov */ public class ChatRoomListImpl implements RegistrationStateChangeListener, ServiceListener { /** * The logger. */ private static final Logger logger = Logger.getLogger(ChatRoomListImpl.class); /** * The list containing all chat servers and rooms. */ private final List<ChatRoomProviderWrapper> providersList = new Vector<ChatRoomProviderWrapper>(); /** * All ChatRoomProviderWrapperListener change listeners registered so far. */ private final List<ChatRoomProviderWrapperListener> providerChangeListeners = new ArrayList<ChatRoomProviderWrapperListener>(); /** * A list of all <tt>ChatRoomListChangeListener</tt>-s. */ private final Vector<ChatRoomListChangeListener> listChangeListeners = new Vector<ChatRoomListChangeListener>(); /** * Constructs and initializes new <tt>ChatRoomListImpl</tt> objects. Adds * the created object as service lister to the bundle context. */ public ChatRoomListImpl() { loadList(); MUCActivator.bundleContext.addServiceListener(this); } /** * Initializes the list of chat rooms. */ public void loadList() { try { ServiceReference[] serRefs = MUCActivator.bundleContext.getServiceReferences( ProtocolProviderService.class.getName(), null); // If we don't have providers at this stage we just return. if (serRefs == null) return; for (ServiceReference serRef : serRefs) { ProtocolProviderService protocolProvider = (ProtocolProviderService) MUCActivator.bundleContext.getService(serRef); Object multiUserChatOpSet = protocolProvider .getOperationSet(OperationSetMultiUserChat.class); if (multiUserChatOpSet != null) { this.addChatProvider(protocolProvider); } } } catch (InvalidSyntaxException e) { logger.error("Failed to obtain service references.", e); } } /** * Adds the given <tt>ChatRoomListChangeListener</tt> that will listen for * all changes of the chat room list data model. * * @param l the listener to add. */ public void addChatRoomListChangeListener(ChatRoomListChangeListener l) { synchronized (listChangeListeners) { listChangeListeners.add(l); } } /** * Removes the given <tt>ChatRoomListChangeListener</tt>. * * @param l the listener to remove. */ public void removeChatRoomListChangeListener(ChatRoomListChangeListener l) { synchronized (listChangeListeners) { listChangeListeners.remove(l); } } /** * Notifies all interested listeners that a change in the chat room list * model has occurred. * @param chatRoomWrapper the chat room wrapper that identifies the chat * room * @param eventID the identifier of the event */ public void fireChatRoomListChangedEvent( ChatRoomWrapper chatRoomWrapper, int eventID) { ChatRoomListChangeEvent evt = new ChatRoomListChangeEvent(chatRoomWrapper, eventID); for (ChatRoomListChangeListener l : listChangeListeners) { l.contentChanged(evt); } } /** * Adds a chat server which is registered and all its existing chat rooms. * * @param pps the <tt>ProtocolProviderService</tt> corresponding to the chat * server */ ChatRoomProviderWrapper addRegisteredChatProvider(ProtocolProviderService pps) { ChatRoomProviderWrapper chatRoomProvider = new ChatRoomProviderWrapperImpl(pps); providersList.add(chatRoomProvider); ConfigurationService configService = MUCActivator.getConfigurationService(); String prefix = "net.java.sip.communicator.impl.gui.accounts"; List<String> accounts = configService.getPropertyNamesByPrefix(prefix, true); for (String accountRootPropName : accounts) { String accountUID = configService.getString(accountRootPropName); if(accountUID.equals(pps .getAccountID().getAccountUniqueID())) { List<String> chatRooms = configService .getPropertyNamesByPrefix( accountRootPropName + ".chatRooms", true); for (String chatRoomPropName : chatRooms) { String chatRoomID = configService.getString(chatRoomPropName); String chatRoomName = configService.getString( chatRoomPropName + ".chatRoomName"); ChatRoomWrapper chatRoomWrapper = new ChatRoomWrapperImpl( chatRoomProvider, chatRoomID, chatRoomName); chatRoomProvider.addChatRoom(chatRoomWrapper); } } } fireProviderWrapperAdded(chatRoomProvider); return chatRoomProvider; } /** * Adds a listener to wait for provider to be registered or unregistered. * * @param pps the <tt>ProtocolProviderService</tt> corresponding to the chat * server */ public void addChatProvider(ProtocolProviderService pps) { if(pps.isRegistered()) addRegisteredChatProvider(pps); else pps.addRegistrationStateChangeListener(this); } /** * Removes the corresponding server and all related chat rooms from this * list. * * @param pps the <tt>ProtocolProviderService</tt> corresponding to the * server to remove */ public void removeChatProvider(ProtocolProviderService pps) { ChatRoomProviderWrapper wrapper = findServerWrapperFromProvider(pps); if (wrapper != null) removeChatProvider(wrapper, true); } /** * Removes the corresponding server and all related chat rooms from this * list. * * @param chatRoomProvider the <tt>ChatRoomProviderWrapper</tt> * corresponding to the server to remove * @param permanently whether to remove any listener * and stored configuration */ private void removeChatProvider(ChatRoomProviderWrapper chatRoomProvider, boolean permanently) { providersList.remove(chatRoomProvider); if(permanently) { chatRoomProvider.getProtocolProvider() .removeRegistrationStateChangeListener(this); ConfigurationService configService = MUCActivator.getConfigurationService(); String prefix = "net.java.sip.communicator.impl.gui.accounts"; AccountID accountID = chatRoomProvider.getProtocolProvider().getAccountID(); // if provider is just disabled don't remove its stored rooms if(!MUCActivator.getAccountManager().getStoredAccounts() .contains(accountID)) { String providerAccountUID = accountID.getAccountUniqueID(); for (String accountRootPropName : configService.getPropertyNamesByPrefix(prefix, true)) { String accountUID = configService.getString(accountRootPropName); if(accountUID.equals(providerAccountUID)) { List<String> chatRooms = configService.getPropertyNamesByPrefix( accountRootPropName + ".chatRooms", true); for (String chatRoomPropName : chatRooms) { configService.setProperty( chatRoomPropName + ".chatRoomName", null); } configService.setProperty(accountRootPropName, null); } } } } for(int i = 0; i < chatRoomProvider.countChatRooms(); i++) { ChatRoomWrapper wrapper = chatRoomProvider.getChatRoom(i); MUCActivator.getUIService().closeChatRoomWindow(wrapper); // clears listeners added by chat room wrapper.removeListeners(); } // clears listeners added by the system chat room chatRoomProvider.getSystemRoomWrapper().removeListeners(); fireProviderWrapperRemoved(chatRoomProvider); } /** * Adds a chat room to this list. * * @param chatRoomWrapper the <tt>ChatRoom</tt> to add */ public void addChatRoom(ChatRoomWrapper chatRoomWrapper) { ChatRoomProviderWrapper chatRoomProvider = chatRoomWrapper.getParentProvider(); if (!chatRoomProvider.containsChatRoom(chatRoomWrapper)) chatRoomProvider.addChatRoom(chatRoomWrapper); if (chatRoomWrapper.isPersistent()) { ConfigurationUtils.saveChatRoom( chatRoomProvider.getProtocolProvider(), chatRoomWrapper.getChatRoomID(), chatRoomWrapper.getChatRoomID(), chatRoomWrapper.getChatRoomName()); } fireChatRoomListChangedEvent( chatRoomWrapper, ChatRoomListChangeEvent.CHAT_ROOM_ADDED); } /** * Removes the given <tt>ChatRoom</tt> from the list of all chat rooms. * * @param chatRoomWrapper the <tt>ChatRoomWrapper</tt> to remove */ public void removeChatRoom(ChatRoomWrapper chatRoomWrapper) { ChatRoomProviderWrapper chatRoomProvider = chatRoomWrapper.getParentProvider(); if (providersList.contains(chatRoomProvider)) { chatRoomProvider.removeChatRoom(chatRoomWrapper); ConfigurationUtils.saveChatRoom( chatRoomProvider.getProtocolProvider(), chatRoomWrapper.getChatRoomID(), null, // The new identifier. null); // The name of the chat room. chatRoomWrapper.removeListeners(); fireChatRoomListChangedEvent( chatRoomWrapper, ChatRoomListChangeEvent.CHAT_ROOM_REMOVED); } } /** * Returns the <tt>ChatRoomWrapper</tt> that correspond to the given * <tt>ChatRoom</tt>. If the list of chat rooms doesn't contain a * corresponding wrapper - returns null. * * @param chatRoom the <tt>ChatRoom</tt> that we're looking for * @return the <tt>ChatRoomWrapper</tt> object corresponding to the given * <tt>ChatRoom</tt> */ public ChatRoomWrapper findChatRoomWrapperFromChatRoom(ChatRoom chatRoom) { for (ChatRoomProviderWrapper provider : providersList) { // check only for the right PP if(!chatRoom.getParentProvider() .equals(provider.getProtocolProvider())) continue; ChatRoomWrapper systemRoomWrapper = provider.getSystemRoomWrapper(); ChatRoom systemRoom = systemRoomWrapper.getChatRoom(); if ((systemRoom != null) && systemRoom.equals(chatRoom)) { return systemRoomWrapper; } else { ChatRoomWrapper chatRoomWrapper = provider.findChatRoomWrapperForChatRoom(chatRoom); if (chatRoomWrapper != null) { // stored chatrooms has no chatroom, but their // id is the same as the chatroom we are searching wrapper // for. Also during reconnect we don't have the same chat // id for another chat room object. if(chatRoomWrapper.getChatRoom() == null || !chatRoomWrapper.getChatRoom().equals(chatRoom)) { chatRoomWrapper.setChatRoom(chatRoom); } return chatRoomWrapper; } } } return null; } /** * Returns the <tt>ChatRoomWrapper</tt> that correspond to the given id of * chat room and provider. If the list of chat rooms doesn't contain a * corresponding wrapper - returns null. * * @param chatRoomID the id of <tt>ChatRoom</tt> that we're looking for * @param pps the procol provider associated with the chat room. * @return the <tt>ChatRoomWrapper</tt> object corresponding to the given id * of the chat room */ public ChatRoomWrapper findChatRoomWrapperFromChatRoomID(String chatRoomID, ProtocolProviderService pps) { for (ChatRoomProviderWrapper provider : providersList) { // check only for the right PP if(!pps.equals(provider.getProtocolProvider())) continue; ChatRoomWrapper systemRoomWrapper = provider.getSystemRoomWrapper(); ChatRoom systemRoom = systemRoomWrapper.getChatRoom(); if ((systemRoom != null) && systemRoom.getIdentifier().equals(chatRoomID)) { return systemRoomWrapper; } else { ChatRoomWrapper chatRoomWrapper = provider.findChatRoomWrapperForChatRoomID(chatRoomID); return chatRoomWrapper; } } return null; } /** * Returns the <tt>ChatRoomProviderWrapper</tt> that correspond to the * given <tt>ProtocolProviderService</tt>. If the list doesn't contain a * corresponding wrapper - returns null. * * @param protocolProvider the protocol provider that we're looking for * @return the <tt>ChatRoomProvider</tt> object corresponding to * the given <tt>ProtocolProviderService</tt> */ public ChatRoomProviderWrapper findServerWrapperFromProvider( ProtocolProviderService protocolProvider) { for(ChatRoomProviderWrapper chatRoomProvider : providersList) { if(chatRoomProvider.getProtocolProvider().equals(protocolProvider)) { return chatRoomProvider; } } return null; } /** * Returns an iterator to the list of chat room providers. * * @return an iterator to the list of chat room providers. */ public Iterator<ChatRoomProviderWrapper> getChatRoomProviders() { return providersList.iterator(); } /** * Adds a ChatRoomProviderWrapperListener to the listener list. * * @param listener the ChatRoomProviderWrapperListener to be added */ public synchronized void addChatRoomProviderWrapperListener( ChatRoomProviderWrapperListener listener) { providerChangeListeners.add(listener); } /** * Removes a ChatRoomProviderWrapperListener from the listener list. * * @param listener the ChatRoomProviderWrapperListener to be removed */ public synchronized void removeChatRoomProviderWrapperListener( ChatRoomProviderWrapperListener listener) { providerChangeListeners.remove(listener); } /** * Fire that chat room provider wrapper was added. * @param provider which was added. */ private void fireProviderWrapperAdded(ChatRoomProviderWrapper provider) { if (providerChangeListeners != null) { for (ChatRoomProviderWrapperListener target : providerChangeListeners) { target.chatRoomProviderWrapperAdded(provider); } } } /** * Fire that chat room provider wrapper was removed. * @param provider which was removed. */ private void fireProviderWrapperRemoved(ChatRoomProviderWrapper provider) { if (providerChangeListeners != null) { for (ChatRoomProviderWrapperListener target : providerChangeListeners) { target.chatRoomProviderWrapperRemoved(provider); } } } /** * Listens for changes of providers registration state, so we can use only * registered providers. * @param evt a <tt>RegistrationStateChangeEvent</tt> which describes the * event that occurred. */ @Override public void registrationStateChanged(RegistrationStateChangeEvent evt) { ProtocolProviderService pps = evt.getProvider(); if (evt.getNewState() == RegistrationState.REGISTERED) { // will use synchronizeOpSetWithLocalContactList // to avoid any concurrency } else if(evt.getNewState() == RegistrationState.UNREGISTERED || evt.getNewState() == RegistrationState.AUTHENTICATION_FAILED || evt.getNewState() == RegistrationState.CONNECTION_FAILED) { ChatRoomProviderWrapper wrapper = findServerWrapperFromProvider(pps); if (wrapper != null) { removeChatProvider(wrapper, false); } } } @Override public void serviceChanged(ServiceEvent event) { // if the event is caused by a bundle being stopped, we don't want to // know if (event.getServiceReference().getBundle().getState() == Bundle.STOPPING) return; Object service = MUCActivator.bundleContext.getService(event .getServiceReference()); // we don't care if the source service is not a protocol provider if (!(service instanceof ProtocolProviderService)) return; ProtocolProviderService protocolProvider = (ProtocolProviderService) service; Object multiUserChatOpSet = protocolProvider .getOperationSet(OperationSetMultiUserChat.class); if (multiUserChatOpSet != null) { if (event.getType() == ServiceEvent.REGISTERED) { addChatProvider(protocolProvider); } else if (event.getType() == ServiceEvent.UNREGISTERING) { removeChatProvider(protocolProvider); } } } }