/** * Permanently removes locally stored message history for the metacontact, remove any recent * contacts if any. */ public void eraseLocallyStoredHistory(MetaContact contact) throws IOException { List<ComparableEvtObj> toRemove = null; synchronized (recentMessages) { toRemove = new ArrayList<ComparableEvtObj>(); Iterator<Contact> iter = contact.getContacts(); while (iter.hasNext()) { Contact item = iter.next(); String id = item.getAddress(); ProtocolProviderService provider = item.getProtocolProvider(); for (ComparableEvtObj msc : recentMessages) { if (msc.getProtocolProviderService().equals(provider) && msc.getContactAddress().equals(id)) { toRemove.add(msc); } } } recentMessages.removeAll(toRemove); } if (recentQuery != null) { for (ComparableEvtObj msc : toRemove) { recentQuery.fireContactRemoved(msc); } } }
/** * When a provider is added. As searching can be slow especially when handling special type of * messages (with subType) this need to be run in new Thread. * * @param provider ProtocolProviderService */ private void handleProviderAddedInSeparateThread( ProtocolProviderService provider, boolean isStatusChanged) { // lets check if we have cached recent messages for this provider, and // fire events if found and are newer synchronized (recentMessages) { List<ComparableEvtObj> cachedRecentMessages = getCachedRecentMessages(provider, isStatusChanged); if (cachedRecentMessages.isEmpty()) { // maybe there is no cached history for this // let's check // load it not from cache, but do a local search Collection<EventObject> res = messageHistoryService.findRecentMessagesPerContact( numberOfMessages, provider.getAccountID().getAccountUniqueID(), null, isSMSEnabled); List<ComparableEvtObj> newMsc = new ArrayList<ComparableEvtObj>(); processEventObjects(res, newMsc, isStatusChanged); addNewRecentMessages(newMsc); for (ComparableEvtObj msc : newMsc) { saveRecentMessageToHistory(msc); } } else addNewRecentMessages(cachedRecentMessages); } }
/** * Process list of event objects. Checks whether message source contact already exist for this * event object, if yes just update it with the new values (not sure whether we should do this, as * it may bring old messages) and if status of provider is changed, init its details, updates its * capabilities. It still adds the found messages source contact to the list of the new contacts, * as later we will detect this and fire update event. If nothing found a new contact is created. * * @param res list of event * @param cachedRecentMessages list of newly created source contacts or already existed but * updated with corresponding event object * @param isStatusChanged whether provider status changed and we are processing */ private void processEventObjects( Collection<EventObject> res, List<ComparableEvtObj> cachedRecentMessages, boolean isStatusChanged) { for (EventObject obj : res) { ComparableEvtObj oldMsg = findRecentMessage(obj, recentMessages); if (oldMsg != null) { oldMsg.update(obj); // update if (isStatusChanged && recentQuery != null) recentQuery.updateCapabilities(oldMsg, obj); // we still add it to cachedRecentMessages // later we will find it is duplicate and will fire // update event if (!cachedRecentMessages.contains(oldMsg)) cachedRecentMessages.add(oldMsg); continue; } oldMsg = findRecentMessage(obj, cachedRecentMessages); if (oldMsg == null) { oldMsg = new ComparableEvtObj(obj); if (isStatusChanged && recentQuery != null) recentQuery.updateCapabilities(oldMsg, obj); cachedRecentMessages.add(oldMsg); } } }
/** * Returns the index of the source contact, in the list of recent messages. * * @param messageSourceContact * @return */ int getIndex(MessageSourceContact messageSourceContact) { synchronized (recentMessages) { for (int i = 0; i < recentMessages.size(); i++) if (recentMessages.get(i).equals(messageSourceContact)) return i; return -1; } }
/** * Searches for contact ids in history of recent messages. * * @param provider * @param after * @return */ List<String> getRecentContactIDs(String provider, Date after) { List<String> res = new ArrayList<String>(); try { History history = getHistory(); if (history != null) { Iterator<HistoryRecord> recs = history.getReader().findLast(NUMBER_OF_MSGS_IN_HISTORY); SimpleDateFormat sdf = new SimpleDateFormat(HistoryService.DATE_FORMAT); while (recs.hasNext()) { HistoryRecord hr = recs.next(); String contact = null; String recordProvider = null; Date timestamp = null; for (int i = 0; i < hr.getPropertyNames().length; i++) { String propName = hr.getPropertyNames()[i]; if (propName.equals(STRUCTURE_NAMES[0])) recordProvider = hr.getPropertyValues()[i]; else if (propName.equals(STRUCTURE_NAMES[1])) contact = hr.getPropertyValues()[i]; else if (propName.equals(STRUCTURE_NAMES[2])) { try { timestamp = sdf.parse(hr.getPropertyValues()[i]); } catch (ParseException e) { timestamp = new Date(Long.parseLong(hr.getPropertyValues()[i])); } } } if (recordProvider == null || contact == null) continue; if (after != null && timestamp != null && timestamp.before(after)) continue; if (recordProvider.equals(provider)) res.add(contact); } } } catch (IOException ex) { logger.error("cannot create recent_messages history", ex); } return res; }
/** * Updates the contact sources in the recent query if any. Done here in order to sync with * recentMessages instance, and to check for already existing instances of contact sources. * Normally called from the query. */ public void updateRecentMessages() { if (recentQuery == null) return; synchronized (recentMessages) { List<SourceContact> currentContactsInQuery = recentQuery.getQueryResults(); for (ComparableEvtObj evtObj : recentMessages) { // the contains will use the correct equals method of // the object evtObj if (!currentContactsInQuery.contains(evtObj)) { MessageSourceContact newSourceContact = new MessageSourceContact(evtObj.getEventObject(), MessageSourceService.this); newSourceContact.initDetails(evtObj.getEventObject()); recentQuery.addQueryResult(newSourceContact); } } } }
/** Permanently removes all locally stored message history, remove recent contacts. */ public void eraseLocallyStoredHistory() throws IOException { List<ComparableEvtObj> toRemove = null; synchronized (recentMessages) { toRemove = new ArrayList<ComparableEvtObj>(recentMessages); recentMessages.clear(); } if (recentQuery != null) { for (ComparableEvtObj msc : toRemove) { recentQuery.fireContactRemoved(msc); } } }
/** * Permanently removes locally stored message history for the chatroom, remove any recent contacts * if any. */ public void eraseLocallyStoredHistory(ChatRoom room) { ComparableEvtObj toRemove = null; synchronized (recentMessages) { for (ComparableEvtObj msg : recentMessages) { if (msg.getRoom() != null && msg.getRoom().equals(room)) { toRemove = msg; break; } } if (toRemove == null) return; recentMessages.remove(toRemove); } if (recentQuery != null) recentQuery.fireContactRemoved(toRemove); }
/** * Searches for entries in cached recent messages in history. * * @param provider the provider which contact messages we will search * @param isStatusChanged is the search because of status changed * @return entries in cached recent messages in history. */ private List<ComparableEvtObj> getCachedRecentMessages( ProtocolProviderService provider, boolean isStatusChanged) { String providerID = provider.getAccountID().getAccountUniqueID(); List<String> recentMessagesContactIDs = getRecentContactIDs( providerID, recentMessages.size() < numberOfMessages ? null : oldestRecentMessage); List<ComparableEvtObj> cachedRecentMessages = new ArrayList<ComparableEvtObj>(); for (String contactID : recentMessagesContactIDs) { Collection<EventObject> res = messageHistoryService.findRecentMessagesPerContact( numberOfMessages, providerID, contactID, isSMSEnabled); processEventObjects(res, cachedRecentMessages, isStatusChanged); } return cachedRecentMessages; }
/** * A provider has been removed. * * @param provider the ProtocolProviderService that has been unregistered. */ void handleProviderRemoved(ProtocolProviderService provider) { // lets remove the recent messages for this provider, and update // with recent messages for the available providers synchronized (recentMessages) { if (provider != null) { List<ComparableEvtObj> removedItems = new ArrayList<ComparableEvtObj>(); for (ComparableEvtObj msc : recentMessages) { if (msc.getProtocolProviderService().equals(provider)) removedItems.add(msc); } recentMessages.removeAll(removedItems); if (!recentMessages.isEmpty()) oldestRecentMessage = recentMessages.get(recentMessages.size() - 1).getTimestamp(); else oldestRecentMessage = null; if (recentQuery != null) { for (ComparableEvtObj msc : removedItems) { recentQuery.fireContactRemoved(msc); } } } // handleProviderRemoved can be invoked due to stopped // history service, if this is the case we do not want to // update messages if (!this.messageHistoryService.isHistoryLoggingEnabled()) return; // lets do the same as we enable provider // for all registered providers and finally fire events List<ComparableEvtObj> contactsToAdd = new ArrayList<ComparableEvtObj>(); for (ProtocolProviderService pps : messageHistoryService.getCurrentlyAvailableProviders()) { contactsToAdd.addAll(getCachedRecentMessages(pps, true)); } addNewRecentMessages(contactsToAdd); } }
/** * Handles new events. * * @param obj the event object * @param provider the provider * @param id the id of the source of the event */ private void handle(EventObject obj, ProtocolProviderService provider, String id) { // check if provider - contact exist update message content synchronized (recentMessages) { ComparableEvtObj existingMsc = null; for (ComparableEvtObj msc : recentMessages) { if (msc.getProtocolProviderService().equals(provider) && msc.getContactAddress().equals(id)) { // update msc.update(obj); updateRecentMessageToHistory(msc); existingMsc = msc; } } if (existingMsc != null) { Collections.sort(recentMessages); oldestRecentMessage = recentMessages.get(recentMessages.size() - 1).getTimestamp(); if (recentQuery != null) { recentQuery.updateContact(existingMsc, existingMsc.getEventObject()); recentQuery.fireContactChanged(existingMsc); } return; } // if missing create source contact // and update recent messages, trim and sort MessageSourceContact newSourceContact = new MessageSourceContact(obj, MessageSourceService.this); newSourceContact.initDetails(obj); // we have already checked for duplicate ComparableEvtObj newMsg = new ComparableEvtObj(obj); recentMessages.add(newMsg); Collections.sort(recentMessages); oldestRecentMessage = recentMessages.get(recentMessages.size() - 1).getTimestamp(); // trim List<ComparableEvtObj> removedItems = null; if (recentMessages.size() > numberOfMessages) { removedItems = new ArrayList<ComparableEvtObj>( recentMessages.subList(numberOfMessages, recentMessages.size())); recentMessages.removeAll(removedItems); } // save saveRecentMessageToHistory(newMsg); // no query nothing to fire if (recentQuery == null) return; // now fire if (removedItems != null) { for (ComparableEvtObj msc : removedItems) { recentQuery.fireContactRemoved(msc); } } recentQuery.addQueryResult(newSourceContact); } }
/** * Add the ComparableEvtObj, newly added will fire new, for existing fire update and when trimming * the list to desired length fire remove for those that were removed * * @param contactsToAdd */ private void addNewRecentMessages(List<ComparableEvtObj> contactsToAdd) { // now find object to fire new, and object to fire remove // let us find duplicates and fire update List<ComparableEvtObj> duplicates = new ArrayList<ComparableEvtObj>(); for (ComparableEvtObj msgToAdd : contactsToAdd) { if (recentMessages.contains(msgToAdd)) { duplicates.add(msgToAdd); // save update updateRecentMessageToHistory(msgToAdd); } } recentMessages.removeAll(duplicates); // now contacts to add has no duplicates, add them all boolean changed = recentMessages.addAll(contactsToAdd); if (changed) { Collections.sort(recentMessages); if (recentQuery != null) { for (ComparableEvtObj obj : duplicates) recentQuery.updateContact(obj, obj.getEventObject()); } } if (!recentMessages.isEmpty()) oldestRecentMessage = recentMessages.get(recentMessages.size() - 1).getTimestamp(); // trim List<ComparableEvtObj> removedItems = null; if (recentMessages.size() > numberOfMessages) { removedItems = new ArrayList<ComparableEvtObj>( recentMessages.subList(numberOfMessages, recentMessages.size())); recentMessages.removeAll(removedItems); } if (recentQuery != null) { // now fire, removed for all that were in the list // and now are removed after trim if (removedItems != null) { for (ComparableEvtObj msc : removedItems) { if (!contactsToAdd.contains(msc)) recentQuery.fireContactRemoved(msc); } } // fire new for all that were added, and not removed after trim for (ComparableEvtObj msc : contactsToAdd) { if ((removedItems == null || !removedItems.contains(msc)) && !duplicates.contains(msc)) { MessageSourceContact newSourceContact = new MessageSourceContact(msc.getEventObject(), MessageSourceService.this); newSourceContact.initDetails(msc.getEventObject()); recentQuery.addQueryResult(newSourceContact); } } // if recent messages were changed, indexes have change lets // fire event for the last element which will reorder the whole // group if needed. if (changed) recentQuery.fireContactChanged(recentMessages.get(recentMessages.size() - 1)); } }