/*
* 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.msghistory;
import static net.java.sip.communicator.service.history.HistoryService.*;
import java.beans.*;
import java.io.*;
import java.text.*;
import java.util.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.history.*;
import net.java.sip.communicator.service.history.event.*;
import net.java.sip.communicator.service.history.event.ProgressEvent;
import net.java.sip.communicator.service.history.records.*;
import net.java.sip.communicator.service.msghistory.*;
import net.java.sip.communicator.service.msghistory.event.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.globalstatus.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.account.*;
import org.jitsi.service.configuration.*;
import org.osgi.framework.*;
/**
* The Message History Service stores messages exchanged through the various
* protocols
* Logs messages for all protocol providers that support basic instant messaging
* (i.e. those that implement OperationSetBasicInstantMessaging).
*
* @author Alexander Pelov
* @author Damian Minkov
* @author Lubomir Marinov
* @author Valentin Martinet
*/
public class MessageHistoryServiceImpl
implements MessageHistoryService,
MessageHistoryAdvancedService,
MessageListener,
ChatRoomMessageListener,
AdHocChatRoomMessageListener,
ServiceListener,
LocalUserChatRoomPresenceListener,
LocalUserAdHocChatRoomPresenceListener
{
/**
* The logger for this class.
*/
private static Logger logger = Logger
.getLogger(MessageHistoryServiceImpl.class);
static String[] STRUCTURE_NAMES
= new String[] { "dir", "msg_CDATA", "msgTyp", "enc", "uid", "sub",
"receivedTimestamp", "msgSubTyp" };
private static HistoryRecordStructure recordStructure =
new HistoryRecordStructure(STRUCTURE_NAMES);
/**
* the field used to search by keywords
*/
private static final String SEARCH_FIELD = "msg";
/**
* Subtype sms to mark sms messages.
*/
static final String MSG_SUBTYPE_SMS = "sms";
/**
* The BundleContext that we got from the OSGI bus.
*/
private BundleContext bundleContext = null;
private HistoryService historyService = null;
private Object syncRoot_HistoryService = new Object();
private Hashtable<MessageHistorySearchProgressListener,
HistorySearchProgressListener> progressListeners =
new Hashtable<MessageHistorySearchProgressListener,
HistorySearchProgressListener>();
private ConfigurationService configService;
private MessageHistoryPropertyChangeListener msgHistoryPropListener;
/**
* Indicates if history logging is enabled.
*/
private static boolean isHistoryLoggingEnabled;
/**
* The message source service, can be null if not enabled.
*/
private MessageSourceService messageSourceService;
/**
* The message source service registration.
*/
private ServiceRegistration messageSourceServiceReg = null;
/**
* Returns the history service.
* @return the history service
*/
public HistoryService getHistoryService()
{
return historyService;
}
/**
* Returns all the messages exchanged by all the contacts
* in the supplied metacontact after the given date
*
* @param contact MetaContact
* @param startDate Date the start date of the conversations
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByStartDate( MetaContact contact,
Date startDate)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
// get the readers for this contact
Map<Contact, HistoryReader> readers = getHistoryReaders(contact);
int recordsCount = countRecords(readers);
for (Map.Entry<Contact, HistoryReader> readerEntry : readers.entrySet())
{
Contact item = readerEntry.getKey();
HistoryReader reader = readerEntry.getValue();
// add the progress listeners
addHistorySearchProgressListeners(reader, recordsCount);
Iterator<HistoryRecord> recs = reader.findByStartDate(startDate);
while (recs.hasNext())
{
result.add(
convertHistoryRecordToMessageEvent(recs.next(), item));
}
}
// now remove this listeners
removeHistorySearchProgressListeners(readers);
return result;
}
private void removeHistorySearchProgressListeners(
Map<?, HistoryReader> readers)
{
for (HistoryReader item : readers.values())
removeHistorySearchProgressListeners(item);
}
/**
* Returns all the messages exchanged by all the contacts
* in the supplied metacontact before the given date
*
* @param contact MetaContact
* @param endDate Date the end date of the conversations
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByEndDate( MetaContact contact,
Date endDate)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
// get the readers for this contact
Map<Contact, HistoryReader> readers = getHistoryReaders(contact);
int recordsCount = countRecords(readers);
for (Map.Entry<Contact, HistoryReader> readerEntry : readers.entrySet())
{
Contact item = readerEntry.getKey();
HistoryReader reader = readerEntry.getValue();
// add the progress listeners
addHistorySearchProgressListeners(reader, recordsCount);
Iterator<HistoryRecord> recs = reader.findByEndDate(endDate);
while (recs.hasNext())
{
result
.add(convertHistoryRecordToMessageEvent(recs.next(), item));
}
}
// now remove this listeners
removeHistorySearchProgressListeners(readers);
return result;
}
/**
* Returns all the messages exchanged by all the contacts
* in the supplied metacontact between the given dates
*
* @param contact MetaContact
* @param startDate Date the start date of the conversations
* @param endDate Date the end date of the conversations
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByPeriod(MetaContact contact,
Date startDate,
Date endDate)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
// get the readers for this contact
Map<Contact, HistoryReader> readers = getHistoryReaders(contact);
int recordsCount = countRecords(readers);
for (Map.Entry<Contact, HistoryReader> readerEntry : readers.entrySet())
{
Contact item = readerEntry.getKey();
HistoryReader reader = readerEntry.getValue();
// add the progress listeners
addHistorySearchProgressListeners(reader, recordsCount);
Iterator<HistoryRecord> recs
= reader.findByPeriod(startDate, endDate);
while (recs.hasNext())
{
result
.add(convertHistoryRecordToMessageEvent(recs.next(), item));
}
}
// now remove this listeners
removeHistorySearchProgressListeners(readers);
return result;
}
/**
* Returns all the messages exchanged by all the contacts
* in the supplied metacontact between the given dates and having the given
* keywords
*
* @param contact MetaContact
* @param startDate Date the start date of the conversations
* @param endDate Date the end date of the conversations
* @param keywords array of keywords
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByPeriod(MetaContact contact,
Date startDate, Date endDate,
String[] keywords)
throws RuntimeException
{
return findByPeriod(contact, startDate, endDate, keywords, false);
}
/**
* Returns all the messages exchanged by all the contacts
* in the supplied metacontact having the given keyword
*
* @param contact MetaContact
* @param keyword keyword
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByKeyword( MetaContact contact,
String keyword)
throws RuntimeException
{
return findByKeyword(contact, keyword, false);
}
/**
* Returns all the messages exchanged by all the contacts
* in the supplied metacontact having the given keywords
*
* @param contact MetaContact
* @param keywords keyword
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByKeywords( MetaContact contact,
String[] keywords)
throws RuntimeException
{
return findByKeywords(contact, keywords, false);
}
/**
* Returns the supplied number of recent messages exchanged by all the
* contacts in the supplied metacontact
*
* @param contact MetaContact
* @param count messages count
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findLast(MetaContact contact, int count)
throws RuntimeException
{
LinkedList<EventObject> result = new LinkedList<EventObject>();
Iterator<Contact> iter = contact.getContacts();
while (iter.hasNext())
{
Contact item = iter.next();
try
{
History history = this.getHistory(null, item);
HistoryReader reader = history.getReader();
Iterator<HistoryRecord> recs = reader.findLast(count);
while (recs.hasNext())
{
result.add(
convertHistoryRecordToMessageEvent(recs.next(), item));
}
}
catch (IOException e)
{
logger.error("Could not read history", e);
}
}
Collections.sort(result, new MessageEventComparator<EventObject>());
int startIndex = result.size() - count;
if(startIndex < 0)
startIndex = 0;
return result.subList(startIndex, result.size());
}
/**
* Checks whether this historyID contains messages of certain type.
* @param historyID
* @param keywords
* @param field
* @param caseSensitive
* @return
* @throws IOException
*/
private boolean hasMessages(HistoryID historyID,
String[] keywords,
String field,
boolean caseSensitive)
throws IOException
{
if(!this.historyService.isHistoryCreated(historyID))
return false;
History history = this.historyService.createHistory(historyID,
recordStructure);
return history.getReader().findLast(
1, keywords, field, caseSensitive).hasNext();
}
/**
* Returns the messages for the recently contacted <tt>count</tt> contacts.
*
* @param count contacts count
* @param providerToFilter can be filtered by provider, or <tt>null</tt> to
* search for all providers
* @param contactToFilter can be filtered by contac, or <tt>null</tt> to
* search for all contacts
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
Collection<EventObject> findRecentMessagesPerContact(
int count, String providerToFilter, String contactToFilter,
boolean isSMSEnabled)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
List<HistoryID> historyIDs=
this.historyService.getExistingHistories(
new String[]{"messages", "default"});
// make the filter provider string to reflect those that were
// used when creating folders
String providerFilterStr = null;
if(providerToFilter != null)
providerFilterStr = HistoryID.readableHash(providerToFilter);
for(HistoryID id : historyIDs)
{
if(result.size() >= count)
break;
try
{
// this history id is: "messages", localId, account, remoteId
if(id.getID().length != 4)
continue;
// filter by protocol provider
if(providerFilterStr != null
&& !id.getID()[2].startsWith(providerFilterStr))
{
continue;
}
if(contactToFilter != null
&& !id.getID()[3].startsWith(contactToFilter))
{
continue;
}
// find contact or chatroom for historyID
Object descriptor = getContactOrRoomByID(
providerToFilter,
id.getID()[3],
id,
isSMSEnabled);
// skip not found contacts, disabled accounts and hidden one
if(descriptor == null)
continue;
History history = this.historyService.createHistory(id,
recordStructure);
HistoryReader reader = history.getReader();
// find last by type
Iterator<HistoryRecord> recs;
if(isSMSEnabled)
{
recs = reader.findLast(
1,
new String[]{MessageHistoryServiceImpl.MSG_SUBTYPE_SMS},
MessageHistoryServiceImpl.STRUCTURE_NAMES[7],
true);
}
else
{
recs = reader.findLast(1);
}
while (recs.hasNext())
{
if(descriptor instanceof Contact)
{
EventObject o = convertHistoryRecordToMessageEvent(
recs.next(), (Contact) descriptor);
result.add(o);
}
if(descriptor instanceof ChatRoom)
{
EventObject o = convertHistoryRecordToMessageEvent(
recs.next(), (ChatRoom) descriptor);
result.add(o);
}
break;
}
}
catch(IOException ex)
{
logger.error("Could not read history", ex);
}
}
return result;
}
/**
* Founds the contact or chat room corresponding this HistoryID. Checks the
* account and then searches for the contact or chat room.
* Will skip hidden and disabled accounts.
*
* @param accountID the account id.
* @param id the contact or room id.
* @return contact or chat room.
*/
private Object getContactOrRoomByID(String accountID,
String id,
HistoryID historyID,
boolean isSMSEnabled)
throws IOException
{
AccountID account = null;
for(AccountID acc : AccountUtils.getStoredAccounts())
{
if( !acc.isHidden()
&& acc.isEnabled()
&& accountID.startsWith(acc.getAccountUniqueID()))
{
account = acc;
break;
}
}
if(account == null)
return null;
ProtocolProviderService pps =
AccountUtils.getRegisteredProviderForAccount(account);
if(pps == null)
return null;
OperationSetPersistentPresence opSetPresence =
pps.getOperationSet(OperationSetPersistentPresence.class);
if(opSetPresence == null)
return null;
Contact contact = opSetPresence.findContactByID(id);
if(isSMSEnabled)
{
//lets check if we have a contact and it has sms messages return it
if(contact != null
&& hasMessages(
historyID,
new String[]{MessageHistoryServiceImpl.MSG_SUBTYPE_SMS},
MessageHistoryServiceImpl.STRUCTURE_NAMES[7],
true))
{
return contact;
}
// we will check only for sms contacts
OperationSetSmsMessaging opSetSMS =
pps.getOperationSet(OperationSetSmsMessaging.class);
// return the contact only if it has stored sms messages
if(opSetSMS == null
|| !hasMessages(
historyID,
new String[]{MessageHistoryServiceImpl.MSG_SUBTYPE_SMS},
MessageHistoryServiceImpl.STRUCTURE_NAMES[7],
true))
{
return null;
}
return opSetSMS.getContact(id);
}
if(contact != null)
return contact;
OperationSetMultiUserChat opSetMuc =
pps.getOperationSet(OperationSetMultiUserChat.class);
if(opSetMuc == null)
return null;
try
{
// will remove the server part
id = id.substring(0, id.lastIndexOf('@'));
return opSetMuc.findRoom(id);
}
catch(Exception e)
{
//logger.error("Cannot find room", e);
return null;
}
}
/**
* Returns the supplied number of recent messages after the given date
* exchanged by all the contacts in the supplied metacontact
*
* @param contact MetaContact
* @param date messages after date
* @param count messages count
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findFirstMessagesAfter( MetaContact contact,
Date date,
int count)
throws RuntimeException
{
LinkedList<EventObject> result = new LinkedList<EventObject>();
Iterator<Contact> iter = contact.getContacts();
while (iter.hasNext())
{
Contact item = iter.next();
try
{
History history = this.getHistory(null, item);
HistoryReader reader = history.getReader();
// date param of method is the one saved in receivedTimestamp
// the method findFirstRecordsAfter compares to the
// attribute timestamp. Most of the times there is 1 or 2 mills
// difference between the two dates. So we will request more
// records from the reader and than will get the needed count
// according to the correct field comparsion (receivedTimestamp)
Iterator<HistoryRecord> recs
= reader.findFirstRecordsAfter(date, count + 4);
while (recs.hasNext())
{
result.add(
convertHistoryRecordToMessageEvent(recs.next(),
item));
}
}
catch (IOException e)
{
logger.error("Could not read history", e);
}
}
// check the dates and skip the starting records which are not ok
int startIx = 0;
Iterator<EventObject> i = result.iterator();
boolean isRecordOK = false;
while (i.hasNext() && !isRecordOK)
{
Object object = i.next();
if(object instanceof MessageDeliveredEvent)
{
isRecordOK =
(((MessageDeliveredEvent)object).getTimestamp().getTime()
> date.getTime());
}
else if(object instanceof MessageReceivedEvent)
{
isRecordOK =
(((MessageReceivedEvent)object).getTimestamp().getTime()
> date.getTime());
}
if(!isRecordOK)
startIx++;
}
Collections.sort(result, new MessageEventComparator<EventObject>());
int toIndex = startIx + count;
if(toIndex > result.size())
toIndex = result.size();
return result.subList(startIx, toIndex);
}
/**
* Returns the supplied number of recent messages before the given date
* exchanged by all the contacts in the supplied metacontact
*
* @param contact MetaContact
* @param date messages before date
* @param count messages count
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findLastMessagesBefore( MetaContact contact,
Date date,
int count)
throws RuntimeException
{
LinkedList<EventObject> result = new LinkedList<EventObject>();
Iterator<Contact> iter = contact.getContacts();
while (iter.hasNext())
{
Contact item = iter.next();
try
{
History history = this.getHistory(null, item);
HistoryReader reader = history.getReader();
Iterator<HistoryRecord> recs
= reader.findLastRecordsBefore(date, count);
while (recs.hasNext())
{
result.add(
convertHistoryRecordToMessageEvent(recs.next(),
item));
}
}
catch (IOException e)
{
logger.error("Could not read history", e);
}
}
Collections.sort(result, new MessageEventComparator<EventObject>());
int startIndex = result.size() - count;
if(startIndex < 0)
startIndex = 0;
return result.subList(startIndex, result.size());
}
/**
* Returns the history by specified local and remote contact
* if one of them is null the default is used
*
* @param localContact Contact
* @param remoteContact Contact
* @return History
* @throws IOException
*/
private History getHistory(Contact localContact, Contact remoteContact)
throws IOException {
String localId = localContact == null ? "default" : localContact
.getAddress();
String remoteId = remoteContact == null ? "default" : remoteContact
.getAddress();
String account = "unkown";
if (remoteContact != null)
account = remoteContact.getProtocolProvider()
.getAccountID().getAccountUniqueID();
HistoryID historyId = HistoryID.createFromRawID(
new String[] { "messages",
localId,
account,
remoteId });
// if this history doesn't exists check to see if old one still exists
// old one is not storing history per account
// if old one exists its converted/moved to the new one
// the new one is in format messages/profile_name/account_uid/contact
// the old one was messages/profile_name/contact
if(!this.historyService.isHistoryCreated(historyId))
{
HistoryID historyId_old = HistoryID.createFromRawID(
new String[] { "messages",
localId,
remoteId });
if(this.historyService.isHistoryCreated(historyId_old))
{
try
{
this.historyService.moveHistory(historyId_old, historyId);
}
catch (IOException iOException)
{
// something is wrong just use the old one
historyId = historyId_old;
}
}
}
return this.historyService.createHistory(historyId, recordStructure);
}
/**
* Returns the history by specified local contact
* (if is null the default is used)
* and by the chat room
*
* @param room The chat room
* @return History the history - created if not existing
* @throws IOException
*/
private History getHistoryForMultiChat(
ChatRoom room)
throws IOException
{
AccountID account = room.getParentProvider().getAccountID();
return this.getHistoryForMultiChat(
null,
account.getAccountUniqueID(),
account.getService(),
room.getName());
}
/**
* Returns the history by specified local contact
* (if is null the default is used)
* and by the ad-hoc chat room
*
* @param room The ad-hoc chat room
* @return History the history - created if not existing
* @throws IOException
*/
private History getHistoryForAdHocMultiChat(
AdHocChatRoom room)
throws IOException
{
AccountID account = room.getParentProvider().getAccountID();
return this.getHistoryForMultiChat(
null,
account.getAccountUniqueID(),
account.getService(),
room.getName());
}
/**
* Returns the history by specified local contact
* (if is null the default is used)
* and by accountUniqueID, channel and server
* used by the multichat account.
*
* @param localContact Contact
* @param account The account UniqueID
* @param server the server used by the account
* @param channel the channel history we are searching for
* @return History the history - created if not existing
* @throws IOException
*/
private History getHistoryForMultiChat(
Contact localContact,
String account,
String server,
String channel)
throws IOException
{
String localId = localContact == null ? "default" : localContact
.getAddress();
HistoryID historyId = HistoryID.createFromRawID(
new String[] { "messages",
localId,
account,
channel + "@" + server });
return this.historyService.createHistory(historyId, recordStructure);
}
/**
* Used to convert HistoryRecord in MessageDeliveredEvent or
* MessageReceivedEvent which are returned by the finder methods
*
* @param hr HistoryRecord
* @param contact Contact
* @return Object
*/
private EventObject convertHistoryRecordToMessageEvent( HistoryRecord hr,
Contact contact)
{
MessageImpl msg = createMessageFromHistoryRecord(hr);
Date timestamp;
// if there is value for date of receiving the message
// this is the event timestamp (this is the date that had came
// from protocol)
// the HistoryRecord timestamp is the timestamp when the record
// was written
Date messageReceivedDate = msg.getMessageReceivedDate();
Date hrTimestamp = hr.getTimestamp();
if (messageReceivedDate.getTime() != 0)
{
// 24*60*60*1000
if(messageReceivedDate.getTime() - hrTimestamp.getTime() > 86400000)
timestamp = hrTimestamp;
else
timestamp = msg.getMessageReceivedDate();
}
else
timestamp = hrTimestamp;
if(msg.isOutgoing)
{
MessageDeliveredEvent evt
= new MessageDeliveredEvent(
msg,
contact,
timestamp);
if(msg.getMsgSubType() != null
&& msg.getMsgSubType().equals(MSG_SUBTYPE_SMS))
{
evt.setSmsMessage(true);
}
return evt;
}
else
{
int eventType = MessageReceivedEvent.CONVERSATION_MESSAGE_RECEIVED;
if(msg.getMsgSubType() != null
&& msg.getMsgSubType().equals(MSG_SUBTYPE_SMS))
{
eventType = MessageReceivedEvent.SMS_MESSAGE_RECEIVED;
}
return new MessageReceivedEvent(
msg,
contact,
timestamp,
eventType);
}
}
/**
* Used to convert HistoryRecord in ChatRoomMessageDeliveredEvent or
* ChatRoomMessageReceivedEvent
* which are returned by the finder methods
*
* @param hr HistoryRecord
* @param room the chat room
* @return Object
*/
private EventObject convertHistoryRecordToMessageEvent(
HistoryRecord hr, ChatRoom room)
{
MessageImpl msg = createMessageFromHistoryRecord(hr);
Date timestamp;
// if there is value for date of receiving the message
// this is the event timestamp (this is the date that had came
// from protocol)
// the HistoryRecord timestamp is the timestamp when the record
// was written
Date messageReceivedDate = msg.getMessageReceivedDate();
Date hrTimestamp = hr.getTimestamp();
if(messageReceivedDate.getTime() != 0)
{
// 24*60*60*1000
if(messageReceivedDate.getTime() - hrTimestamp.getTime() > 86400000)
timestamp = hrTimestamp;
else
timestamp = msg.getMessageReceivedDate();
}
else
timestamp = hrTimestamp;
// 5 is the index of the subject in the structure
String fromStr = hr.getPropertyValues()[5];
ChatRoomMember from = new ChatRoomMemberImpl(fromStr, room, null);
if(msg.isOutgoing)
{
return new ChatRoomMessageDeliveredEvent(
room,
timestamp,
msg,
ChatRoomMessageDeliveredEvent
.CONVERSATION_MESSAGE_DELIVERED);
}
else
return new ChatRoomMessageReceivedEvent(
room,
from,
timestamp,
msg,
ChatRoomMessageReceivedEvent
.CONVERSATION_MESSAGE_RECEIVED);
}
private MessageImpl createMessageFromHistoryRecord(HistoryRecord hr)
{
// History structure
// 0 - dir
// 1 - msg_CDATA
// 2 - msgTyp
// 3 - enc
// 4- uid
// 5 - sub
// 6 - receivedTimestamp
// 7 - msgSubType
String textContent = null;
String contentType = null;
String contentEncoding = null;
String messageUID = null;
String subject = null;
boolean isOutgoing = false;
Date messageReceivedDate = new Date(0);
String msgSubType = null;
SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
for (int i = 0; i < hr.getPropertyNames().length; i++)
{
String propName = hr.getPropertyNames()[i];
if (propName.equals("msg") || propName.equals(STRUCTURE_NAMES[1]))
textContent = hr.getPropertyValues()[i];
else if (propName.equals(STRUCTURE_NAMES[2]))
contentType = hr.getPropertyValues()[i];
else if (propName.equals(STRUCTURE_NAMES[3]))
contentEncoding = hr.getPropertyValues()[i];
else if (propName.equals(STRUCTURE_NAMES[4]))
messageUID = hr.getPropertyValues()[i];
else if (propName.equals(STRUCTURE_NAMES[5]))
subject = hr.getPropertyValues()[i];
else if (propName.equals(STRUCTURE_NAMES[0]))
{
if (hr.getPropertyValues()[i].equals("in"))
isOutgoing = false;
else if (hr.getPropertyValues()[i].equals("out"))
isOutgoing = true;
}
else if (propName.equals(STRUCTURE_NAMES[6]))
{
try
{
messageReceivedDate = sdf.parse(hr.getPropertyValues()[i]);
}
catch (ParseException e)
{
messageReceivedDate =
new Date(Long.parseLong(hr.getPropertyValues()[i]));
}
}
else if (propName.equals(STRUCTURE_NAMES[7]))
{
msgSubType = hr.getPropertyValues()[i];
}
}
return new MessageImpl(textContent, contentType, contentEncoding,
subject, messageUID, isOutgoing, messageReceivedDate, msgSubType);
}
/**
* Starts the service. Check the current registered protocol providers
* which supports BasicIM and adds message listener to them
*
* @param bc BundleContext
*/
public void start(BundleContext bc)
{
this.bundleContext = bc;
ServiceReference refConfig = bundleContext.getServiceReference(
ConfigurationService.class.getName());
configService = (ConfigurationService)
bundleContext.getService(refConfig);
// Check if the message history is enabled in the configuration
// service, and if not do not register the service.
boolean isMessageHistoryEnabled = configService.getBoolean(
MessageHistoryService.PNAME_IS_MESSAGE_HISTORY_ENABLED,
Boolean.parseBoolean(
MessageHistoryActivator.getResources().getSettingsString(
MessageHistoryService.PNAME_IS_MESSAGE_HISTORY_ENABLED))
);
// We're adding a property change listener in order to
// listen for modifications of the isMessageHistoryEnabled property.
msgHistoryPropListener = new MessageHistoryPropertyChangeListener();
// Load the "IS_MESSAGE_HISTORY_ENABLED" property.
isHistoryLoggingEnabled = configService.getBoolean(
MessageHistoryService.PNAME_IS_MESSAGE_HISTORY_ENABLED,
Boolean.parseBoolean(UtilActivator
.getResources().getSettingsString(
MessageHistoryService.PNAME_IS_MESSAGE_HISTORY_ENABLED))
);
configService.addPropertyChangeListener(
MessageHistoryService.PNAME_IS_MESSAGE_HISTORY_ENABLED,
msgHistoryPropListener);
if (isMessageHistoryEnabled)
{
if (logger.isDebugEnabled())
logger.debug("Starting the msg history implementation.");
this.loadMessageHistoryService();
}
}
/**
* Loads and registers the contact source service.
*/
private void loadRecentMessages()
{
this.messageSourceService = new MessageSourceService(this);
messageSourceServiceReg = bundleContext.registerService(
ContactSourceService.class.getName(),
messageSourceService, null);
MessageHistoryActivator.getContactListService()
.addMetaContactListListener(this.messageSourceService);
}
/**
* Unloads the contact source service.
*/
private void stopRecentMessages()
{
if(messageSourceServiceReg != null)
{
MessageHistoryActivator.getContactListService()
.removeMetaContactListListener(this.messageSourceService);
messageSourceServiceReg.unregister();
messageSourceServiceReg = null;
this.messageSourceService = null;
}
}
/**
* Stops the service.
*
* @param bc BundleContext
*/
public void stop(BundleContext bc)
{
if (configService != null)
configService.removePropertyChangeListener(msgHistoryPropListener);
stopMessageHistoryService();
}
// //////////////////////////////////////////////////////////////////////////
// MessageListener implementation methods
public void messageReceived(MessageReceivedEvent evt)
{
this.writeMessage(
"in",
null,
evt.getSourceContact(),
evt.getSourceMessage(),
evt.getTimestamp(),
evt.getEventType() == MessageReceivedEvent.SMS_MESSAGE_RECEIVED);
}
public void messageDelivered(MessageDeliveredEvent evt)
{
this.writeMessage(
"out",
null,
evt.getDestinationContact(),
evt.getSourceMessage(),
evt.getTimestamp(),
evt.isSmsMessage());
}
public void messageDeliveryFailed(MessageDeliveryFailedEvent evt)
{
}
// //////////////////////////////////////////////////////////////////////////
// ChatRoomMessageListener implementation methods
public void messageReceived(ChatRoomMessageReceivedEvent evt)
{
if(!isHistoryLoggingEnabled(
evt.getSourceChatRoom().getIdentifier()))
{
// logging is switched off for this particular chat room
return;
}
try
{
// ignore non conversation messages
if(evt.getEventType() !=
ChatRoomMessageReceivedEvent.CONVERSATION_MESSAGE_RECEIVED)
return;
History history = this.getHistoryForMultiChat(
evt.getSourceChatRoom());
// if this is chat room message history on every room enter
// we can receive the same latest history messages and this
// will just fill the history on every join
if(evt.isHistoryMessage())
{
Collection<EventObject> c =
findFirstMessagesAfter(evt.getSourceChatRoom(),
new Date(evt.getTimestamp().getTime() - 10000),
20);
Iterator<EventObject> iter = c.iterator();
boolean isPresent = false;
while(iter.hasNext())
{
EventObject e = iter.next();
if(e instanceof ChatRoomMessageReceivedEvent)
{
ChatRoomMessageReceivedEvent cev =
(ChatRoomMessageReceivedEvent)e;
if( evt.getSourceChatRoomMember().getContactAddress()
!= null
&& evt.getSourceChatRoomMember().getContactAddress()
.equals(cev.getSourceChatRoomMember()
.getContactAddress())
&& evt.getTimestamp() != null
&& evt.getTimestamp().equals(cev.getTimestamp()))
{
isPresent = true;
break;
}
// also check and message content
Message m1 = cev.getMessage();
Message m2 = evt.getMessage();
if(m1 != null && m2 != null
&& m1.getContent().equals(m2.getContent()))
{
isPresent = true;
break;
}
}
}
if (isPresent)
return;
}
writeMessage(history, "in", evt.getSourceChatRoomMember(),
evt.getMessage(), evt.getTimestamp());
} catch (IOException e)
{
logger.error("Could not add message to history", e);
}
}
public void messageDelivered(ChatRoomMessageDeliveredEvent evt)
{
try
{
if(!isHistoryLoggingEnabled(
evt.getSourceChatRoom().getIdentifier()))
{
// logging is switched off for this particular chat room
return;
}
History history = this.getHistoryForMultiChat(
evt.getSourceChatRoom());
// if this is chat room message history on every room enter
// we can receive the same latest history messages and this
// will just fill the history on every join
if(evt.isHistoryMessage())
{
Collection<EventObject> c =
findFirstMessagesAfter(evt.getSourceChatRoom(),
new Date(evt.getTimestamp().getTime() - 10000),
20);
Iterator<EventObject> iter = c.iterator();
boolean isPresent = false;
while(iter.hasNext())
{
EventObject e = iter.next();
if(e instanceof ChatRoomMessageDeliveredEvent)
{
ChatRoomMessageDeliveredEvent cev =
(ChatRoomMessageDeliveredEvent)e;
if(evt.getTimestamp() != null
&& evt.getTimestamp().equals(cev.getTimestamp()))
{
isPresent = true;
break;
}
// also check and message content
Message m1 = cev.getMessage();
Message m2 = evt.getMessage();
if(m1 != null && m2 != null
&& m1.getContent().equals(m2.getContent()))
{
isPresent = true;
break;
}
}
}
if (isPresent)
return;
}
writeMessage(
history, "out", evt.getMessage(), evt.getTimestamp(), false);
} catch (IOException e)
{
logger.error("Could not add message to history", e);
}
}
public void messageDeliveryFailed(ChatRoomMessageDeliveryFailedEvent evt)
{
}
/**
* Writes message to the history
* @param direction String direction of the message
* @param source The source Contact
* @param destination The destination Contact
* @param message Message message to be written
* @param messageTimestamp Date this is the timestamp when was message
* received that came from the protocol provider
* @param isSmsSubtype whether message to write is an sms
*/
private void writeMessage(
String direction,
Contact source,
Contact destination,
Message message,
Date messageTimestamp,
boolean isSmsSubtype)
{
try
{
MetaContact metaContact = MessageHistoryActivator
.getContactListService().findMetaContactByContact(destination);
if(metaContact != null
&& !isHistoryLoggingEnabled(
metaContact.getMetaUID()))
{
// logging is switched off for this particular contact
return;
}
History history = this.getHistory(source, destination);
writeMessage(
history, direction, message, messageTimestamp, isSmsSubtype);
} catch (IOException e)
{
logger.error("Could not add message to history", e);
}
}
/**
* Writes message to the history
* @param history The history to which will write the message
* @param direction coming from
* @param message Message
* @param messageTimestamp Date this is the timestamp when was message
* received that came from the protocol provider
*/
private void writeMessage(History history, String direction,
Message message, Date messageTimestamp, boolean isSmsSubtype)
{
try {
HistoryWriter historyWriter = history.getWriter();
SimpleDateFormat sdf
= new SimpleDateFormat(HistoryService.DATE_FORMAT);
historyWriter.addRecord(new String[] { direction,
message.getContent(), message.getContentType(),
message.getEncoding(), message.getMessageUID(),
message.getSubject(), sdf.format(messageTimestamp),
isSmsSubtype ? MSG_SUBTYPE_SMS : null},
new Date()); // this date is when the history record is written
} catch (IOException e)
{
logger.error("Could not add message to history", e);
}
}
/**
* Writes message to the history
* @param history The history to which will write the message
* @param direction the direction of the message.
* @param from coming from
* @param message Message
* @param messageTimestamp Date this is the timestamp when was message received
* that came from the protocol provider
*/
private void writeMessage(History history, String direction,
ChatRoomMember from,
Message message, Date messageTimestamp)
{
try {
// mising from, strange messages, most probably a history
// coming from server and probably already written
if(from == null)
return;
HistoryWriter historyWriter = history.getWriter();
SimpleDateFormat sdf
= new SimpleDateFormat(HistoryService.DATE_FORMAT);
historyWriter.addRecord(new String[] { direction,
message.getContent(), message.getContentType(),
message.getEncoding(), message.getMessageUID(),
from.getContactAddress(),
sdf.format(messageTimestamp),
null},
new Date()); // this date is when the history record is written
} catch (IOException e)
{
logger.error("Could not add message to history", e);
}
}
/**
* Writes a message to the history.
* @param history The history to which will write the message
* @param direction the direction of the message.
* @param from coming from
* @param message Message
* @param messageTimestamp Date this is the timestamp when was message received
* that came from the protocol provider
*/
private void writeMessage(History history, String direction,
Contact from,
Message message, Date messageTimestamp)
{
try
{
HistoryWriter historyWriter = history.getWriter();
SimpleDateFormat sdf
= new SimpleDateFormat(HistoryService.DATE_FORMAT);
historyWriter.addRecord(new String[] { direction,
message.getContent(), message.getContentType(),
message.getEncoding(), message.getMessageUID(),
from.getAddress(),
sdf.format(messageTimestamp),
null},
new Date()); // this date is when the history record is written
} catch (IOException e)
{
logger.error("Could not add message to history", e);
}
}
/**
* Inserts message to the history. Allows to update the laready saved
* history.
* @param direction String direction of the message in or out.
* @param source The source Contact
* @param destination The destination Contact
* @param message Message message to be written
* @param messageTimestamp Date this is the timestamp when was message
* received that came from the protocol provider
* @param isSmsSubtype whether message to write is an sms
*/
public void insertMessage(
String direction,
Contact source,
Contact destination,
Message message,
Date messageTimestamp,
boolean isSmsSubtype)
{
try
{
MetaContact metaContact = MessageHistoryActivator
.getContactListService().findMetaContactByContact(destination);
if(metaContact != null
&& !isHistoryLoggingEnabled(
metaContact.getMetaUID()))
{
// logging is switched off for this particular contact
return;
}
History history = this.getHistory(source, destination);
HistoryWriter historyWriter = history.getWriter();
SimpleDateFormat sdf
= new SimpleDateFormat(HistoryService.DATE_FORMAT);
historyWriter.insertRecord(new String[]{direction,
message.getContent(), message.getContentType(),
message.getEncoding(), message.getMessageUID(),
message.getSubject(), sdf.format(messageTimestamp),
isSmsSubtype ? MSG_SUBTYPE_SMS : null},
messageTimestamp,
STRUCTURE_NAMES[6]);
// this date is when the history record to be written
// as we are inserting
} catch (IOException e)
{
logger.error("Could not add message to history", e);
}
}
// //////////////////////////////////////////////////////////////////////////
/**
* Set the configuration service.
*
* @param historyService HistoryService
* @throws IOException
* @throws IllegalArgumentException
*/
public void setHistoryService(HistoryService historyService)
throws IllegalArgumentException, IOException {
synchronized (this.syncRoot_HistoryService)
{
this.historyService = historyService;
if (logger.isDebugEnabled())
logger.debug("New history service registered.");
}
}
/**
* Remove a configuration service.
*
* @param historyService HistoryService
*/
public void unsetHistoryService(HistoryService historyService)
{
synchronized (this.syncRoot_HistoryService)
{
if (this.historyService == historyService)
{
this.historyService = null;
if (logger.isDebugEnabled())
logger.debug("History service unregistered.");
}
}
}
/**
* When new protocol provider is registered we check
* does it supports BasicIM and if so add a listener to it
*
* @param serviceEvent ServiceEvent
*/
public void serviceChanged(ServiceEvent serviceEvent)
{
Object sService = bundleContext.getService(serviceEvent.getServiceReference());
if (logger.isTraceEnabled())
logger.trace("Received a service event for: " + sService.getClass().getName());
// we don't care if the source service is not a protocol provider
if (! (sService instanceof ProtocolProviderService))
{
return;
}
if (logger.isDebugEnabled())
logger.debug("Service is a protocol provider.");
if (serviceEvent.getType() == ServiceEvent.REGISTERED)
{
if (logger.isDebugEnabled())
logger.debug("Handling registration of a new Protocol Provider.");
this.handleProviderAdded((ProtocolProviderService)sService);
}
else if (serviceEvent.getType() == ServiceEvent.UNREGISTERING)
{
this.handleProviderRemoved( (ProtocolProviderService) sService);
}
}
/**
* Used to attach the Message History Service to existing or
* just registered protocol provider. Checks if the provider has implementation
* of OperationSetBasicInstantMessaging
*
* @param provider ProtocolProviderService
*/
private void handleProviderAdded(ProtocolProviderService provider)
{
if (logger.isDebugEnabled())
logger.debug("Adding protocol provider "
+ provider.getProtocolDisplayName());
// check whether the provider has a basic im operation set
OperationSetBasicInstantMessaging opSetIm =
provider.getOperationSet(OperationSetBasicInstantMessaging.class);
if (opSetIm != null)
{
opSetIm.addMessageListener(this);
if(this.messageSourceService != null)
opSetIm.addMessageListener(messageSourceService);
}
else
{
if (logger.isTraceEnabled())
logger.trace("Service did not have a im op. set.");
}
OperationSetSmsMessaging opSetSMS =
provider.getOperationSet(OperationSetSmsMessaging.class);
if (opSetSMS != null)
{
opSetSMS.addMessageListener(this);
if(this.messageSourceService != null)
opSetSMS.addMessageListener(messageSourceService);
}
else
{
if (logger.isTraceEnabled())
logger.trace("Service did not have a sms op. set.");
}
OperationSetMultiUserChat opSetMultiUChat =
provider.getOperationSet(OperationSetMultiUserChat.class);
if (opSetMultiUChat != null)
{
Iterator<ChatRoom> iter =
opSetMultiUChat.getCurrentlyJoinedChatRooms().iterator();
while(iter.hasNext())
{
ChatRoom room = iter.next();
room.addMessageListener(this);
}
opSetMultiUChat.addPresenceListener(this);
if(messageSourceService != null)
opSetMultiUChat.addPresenceListener(messageSourceService);
}
else
{
if (logger.isTraceEnabled())
logger.trace("Service did not have a multi im op. set.");
}
if(messageSourceService != null)
{
OperationSetPresence opSetPresence =
provider.getOperationSet(OperationSetPresence.class);
if (opSetPresence != null)
{
opSetPresence
.addContactPresenceStatusListener(messageSourceService);
opSetPresence
.addProviderPresenceStatusListener(messageSourceService);
opSetPresence.addSubscriptionListener(messageSourceService);
}
messageSourceService.handleProviderAdded(provider, false);
OperationSetContactCapabilities capOpSet
= provider.getOperationSet(OperationSetContactCapabilities.class);
if(capOpSet != null)
{
capOpSet.addContactCapabilitiesListener(messageSourceService);
}
}
}
/**
* Removes the specified provider from the list of currently known providers
* and ignores all the messages exchanged by it
*
* @param provider the ProtocolProviderService that has been unregistered.
*/
private void handleProviderRemoved(ProtocolProviderService provider)
{
OperationSetBasicInstantMessaging opSetIm =
provider.getOperationSet(OperationSetBasicInstantMessaging.class);
if (opSetIm != null)
{
opSetIm.removeMessageListener(this);
if(this.messageSourceService != null)
opSetIm.removeMessageListener(messageSourceService);
}
OperationSetSmsMessaging opSetSMS =
provider.getOperationSet(OperationSetSmsMessaging.class);
if (opSetSMS != null)
{
opSetSMS.removeMessageListener(this);
if(this.messageSourceService != null)
opSetSMS.removeMessageListener(messageSourceService);
}
OperationSetMultiUserChat opSetMultiUChat =
provider.getOperationSet(OperationSetMultiUserChat.class);
if (opSetMultiUChat != null)
{
Iterator<ChatRoom> iter =
opSetMultiUChat.getCurrentlyJoinedChatRooms().iterator();
while(iter.hasNext())
{
ChatRoom room = iter.next();
room.removeMessageListener(this);
}
opSetMultiUChat.removePresenceListener(this);
if(messageSourceService != null)
opSetMultiUChat.removePresenceListener(messageSourceService);
}
if(messageSourceService != null)
{
OperationSetPresence opSetPresence =
provider.getOperationSet(OperationSetPresence.class);
if(opSetPresence != null)
{
opSetPresence
.removeContactPresenceStatusListener(messageSourceService);
opSetPresence
.removeProviderPresenceStatusListener(messageSourceService);
opSetPresence.removeSubscriptionListener(messageSourceService);
}
messageSourceService.handleProviderRemoved(provider);
OperationSetContactCapabilities capOpSet
= provider.getOperationSet(OperationSetContactCapabilities.class);
if(capOpSet != null)
{
capOpSet.removeContactCapabilitiesListener(messageSourceService);
}
}
}
/**
* Called to notify interested parties that a change in our presence in
* a chat room has occured. Changes may include us being kicked, join,
* left.
* @param evt the <tt>LocalUserChatRoomPresenceChangeEvent</tt> instance
* containing the chat room and the type, and reason of the change
*/
public void localUserPresenceChanged(LocalUserChatRoomPresenceChangeEvent evt)
{
if(evt.getEventType() ==
LocalUserChatRoomPresenceChangeEvent.LOCAL_USER_JOINED)
{
if (!evt.getChatRoom().isSystem())
{
evt.getChatRoom().addMessageListener(this);
if(this.messageSourceService != null)
evt.getChatRoom().addMessageListener(messageSourceService);
}
}
else
{
evt.getChatRoom().removeMessageListener(this);
if(this.messageSourceService != null)
evt.getChatRoom().removeMessageListener(messageSourceService);
}
}
/**
* Adding progress listener for monitoring progress of search process
*
* @param listener HistorySearchProgressListener
*/
public void addSearchProgressListener(MessageHistorySearchProgressListener
listener)
{
synchronized(progressListeners){
HistorySearchProgressListener wrapperListener =
new SearchProgressWrapper(listener);
progressListeners.put(listener, wrapperListener);
}
}
/**
* Removing progress listener
*
* @param listener HistorySearchProgressListener
*/
public void removeSearchProgressListener(
MessageHistorySearchProgressListener listener)
{
synchronized(progressListeners){
progressListeners.remove(listener);
}
}
/**
* Add the registered MessageHistorySearchProgressListeners
* to the given HistoryReader
*
* @param reader HistoryReader
* @param countRecords number of records will search
*/
private void addHistorySearchProgressListeners(
HistoryReader reader, int countRecords)
{
synchronized(progressListeners)
{
Iterator<HistorySearchProgressListener> iter = progressListeners.values().iterator();
while (iter.hasNext())
{
SearchProgressWrapper l =
(SearchProgressWrapper) iter.next();
l.setCurrentValues(reader, countRecords);
reader.addSearchProgressListener(l);
}
}
}
/**
* Removes the registered MessageHistorySearchProgressListeners
* from the given HistoryReader
*
* @param reader HistoryReader
*/
private void removeHistorySearchProgressListeners(HistoryReader reader)
{
synchronized(progressListeners)
{
Iterator<HistorySearchProgressListener> iter = progressListeners.values().iterator();
while (iter.hasNext())
{
SearchProgressWrapper l =
(SearchProgressWrapper) iter.next();
l.clear();
reader.removeSearchProgressListener(l);
}
}
}
/**
* Returns all the messages exchanged by all the contacts
* in the supplied metacontact between the given dates and having the given
* keywords
*
* @param contact MetaContact
* @param startDate Date the start date of the conversations
* @param endDate Date the end date of the conversations
* @param keywords array of keywords
* @param caseSensitive is keywords search case sensitive
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByPeriod(MetaContact contact, Date startDate,
Date endDate, String[] keywords,
boolean caseSensitive)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
// get the readers for this contact
Map<Contact, HistoryReader> readers = getHistoryReaders(contact);
int recordsCount = countRecords(readers);
for (Map.Entry<Contact, HistoryReader> readerEntry : readers.entrySet())
{
Contact item = readerEntry.getKey();
HistoryReader reader = readerEntry.getValue();
// add the progress listeners
addHistorySearchProgressListeners(reader, recordsCount);
Iterator<HistoryRecord> recs = reader.findByPeriod(startDate, endDate, keywords,
SEARCH_FIELD, caseSensitive);
while (recs.hasNext())
{
result.add(convertHistoryRecordToMessageEvent(recs.next(), item));
}
}
// now remove this listeners
removeHistorySearchProgressListeners(readers);
return result;
}
/**
* Returns all the messages exchanged by all the contacts
* in the supplied metacontact having the given keyword
*
* @param contact MetaContact
* @param keyword keyword
* @param caseSensitive is keywords search case sensitive
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByKeyword(MetaContact contact, String keyword,
boolean caseSensitive)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
// get the readers for this contact
Map<Contact, HistoryReader> readers = getHistoryReaders(contact);
int recordsCount = countRecords(readers);
for (Map.Entry<Contact, HistoryReader> readerEntry : readers.entrySet())
{
Contact item = readerEntry.getKey();
HistoryReader reader = readerEntry.getValue();
// add the progress listeners
addHistorySearchProgressListeners(reader, recordsCount);
Iterator<HistoryRecord> recs = reader.
findByKeyword(keyword, SEARCH_FIELD, caseSensitive);
while (recs.hasNext())
{
result.add(convertHistoryRecordToMessageEvent(recs.next(), item));
}
}
// now remove this listeners
removeHistorySearchProgressListeners(readers);
return result;
}
/**
* Returns all the messages exchanged by all the contacts
* in the supplied metacontact having the given keywords
*
* @param contact MetaContact
* @param keywords keyword
* @param caseSensitive is keywords search case sensitive
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByKeywords(MetaContact contact, String[] keywords,
boolean caseSensitive)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
// get the readers for this contact
Map<Contact, HistoryReader> readers = getHistoryReaders(contact);
int recordsCount = countRecords(readers);
for (Map.Entry<Contact, HistoryReader> readerEntry : readers.entrySet())
{
Contact item = readerEntry.getKey();
HistoryReader reader = readerEntry.getValue();
// add the progress listeners
addHistorySearchProgressListeners(reader, recordsCount);
Iterator<HistoryRecord> recs = reader.
findByKeywords(keywords, SEARCH_FIELD, caseSensitive);
while (recs.hasNext())
{
result.add(convertHistoryRecordToMessageEvent(recs.next(), item));
}
}
// now remove this listeners
removeHistorySearchProgressListeners(readers);
return result;
}
/**
* Gets all the history readers for the contacts in the given MetaContact
* @param contact MetaContact
* @return Hashtable
*/
private Map<Contact, HistoryReader> getHistoryReaders(MetaContact contact)
{
Map<Contact, HistoryReader> readers = new Hashtable<Contact, HistoryReader>();
Iterator<Contact> iter = contact.getContacts();
while (iter.hasNext())
{
Contact item = iter.next();
try
{
History history = this.getHistory(null, item);
readers.put(item, history.getReader());
}
catch (IOException e)
{
logger.error("Could not read history", e);
}
}
return readers;
}
/**
* Total count of records for supplied history readers will read through
*
* @param readers hashtable with pairs contact <-> history reader
* @return the number of searched messages
* @throws UnsupportedOperationException
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
public int countRecords(Map<?, HistoryReader> readers)
{
int result = 0;
for (HistoryReader r : readers.values())
result += r.countRecords();
return result;
}
/**
* Returns all the messages exchanged in the supplied
* chat room after the given date
*
* @param room The chat room
* @param startDate Date the start date of the conversations
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByStartDate(ChatRoom room, Date startDate)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
try
{
// get the readers for this room
HistoryReader reader =
this.getHistoryForMultiChat(room).getReader();
// add the progress listeners
addHistorySearchProgressListeners(reader, 1);
Iterator<HistoryRecord> recs = reader.findByStartDate(startDate);
while (recs.hasNext())
{
result.add(convertHistoryRecordToMessageEvent(recs.next(), room));
}
removeHistorySearchProgressListeners(reader);
} catch (IOException e)
{
logger.error("Could not read history", e);
}
return result;
}
/**
* Returns all the messages exchanged
* in the supplied chat room before the given date
*
* @param room The chat room
* @param endDate Date the end date of the conversations
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByEndDate(ChatRoom room, Date endDate)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
try
{
// get the readers for this room
HistoryReader reader =
this.getHistoryForMultiChat(room).getReader();
// add the progress listeners
addHistorySearchProgressListeners(reader, 1);
Iterator<HistoryRecord> recs = reader.findByEndDate(endDate);
while (recs.hasNext())
{
result.add(convertHistoryRecordToMessageEvent(recs.next(), room));
}
removeHistorySearchProgressListeners(reader);
} catch (IOException e)
{
logger.error("Could not read history", e);
}
return result;
}
/**
* Returns all the messages exchanged
* in the supplied chat room between the given dates
*
* @param room The chat room
* @param startDate Date the start date of the conversations
* @param endDate Date the end date of the conversations
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByPeriod(ChatRoom room, Date startDate, Date endDate)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
try
{
// get the readers for this room
HistoryReader reader =
this.getHistoryForMultiChat(room).getReader();
// add the progress listeners
addHistorySearchProgressListeners(reader, 1);
Iterator<HistoryRecord> recs = reader.findByPeriod(startDate, endDate);
while (recs.hasNext())
{
result.add(convertHistoryRecordToMessageEvent(recs.next(), room));
}
removeHistorySearchProgressListeners(reader);
} catch (IOException e)
{
logger.error("Could not read history", e);
}
return result;
}
/**
* Returns all the messages exchanged
* in the supplied chat room between the given dates and having the given
* keywords
*
* @param room The chat room
* @param startDate Date the start date of the conversations
* @param endDate Date the end date of the conversations
* @param keywords array of keywords
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByPeriod(ChatRoom room,
Date startDate, Date endDate, String[] keywords)
throws RuntimeException
{
return findByPeriod(room, startDate, endDate, keywords, false);
}
/**
* Returns all the messages exchanged
* in the supplied chat room between the given dates and having the given
* keywords
*
* @param room The chat room
* @param startDate Date the start date of the conversations
* @param endDate Date the end date of the conversations
* @param keywords array of keywords
* @param caseSensitive is keywords search case sensitive
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByPeriod(ChatRoom room, Date startDate,
Date endDate, String[] keywords, boolean caseSensitive)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
try
{
// get the readers for this room
HistoryReader reader =
this.getHistoryForMultiChat(room).getReader();
// add the progress listeners
addHistorySearchProgressListeners(reader, 1);
Iterator<HistoryRecord> recs
= reader.findByPeriod(startDate, endDate, keywords,
SEARCH_FIELD, caseSensitive);
while (recs.hasNext())
{
result.add(convertHistoryRecordToMessageEvent(recs.next(), room));
}
removeHistorySearchProgressListeners(reader);
} catch (IOException e)
{
logger.error("Could not read history", e);
}
return result;
}
/**
* Returns all the messages exchanged
* in the supplied room having the given keyword
*
* @param room The Chat room
* @param keyword keyword
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByKeyword(ChatRoom room, String keyword)
throws RuntimeException
{
return findByKeyword(room, keyword, false);
}
/**
* Returns all the messages exchanged
* in the supplied chat room having the given keyword
*
* @param room The chat room
* @param keyword keyword
* @param caseSensitive is keywords search case sensitive
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByKeyword(ChatRoom room, String keyword,
boolean caseSensitive)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
try
{
// get the readers for this room
HistoryReader reader =
this.getHistoryForMultiChat(room).getReader();
// add the progress listeners
addHistorySearchProgressListeners(reader, 1);
Iterator<HistoryRecord> recs = reader.
findByKeyword(keyword, SEARCH_FIELD, caseSensitive);
while (recs.hasNext())
{
result
.add(convertHistoryRecordToMessageEvent(recs.next(), room));
}
removeHistorySearchProgressListeners(reader);
} catch (IOException e)
{
logger.error("Could not read history", e);
}
return result;
}
/**
* Returns all the messages exchanged
* in the supplied chat room having the given keywords
*
* @param room The chat room
* @param keywords keyword
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByKeywords( ChatRoom room,
String[] keywords)
throws RuntimeException
{
return findByKeywords(room, keywords, false);
}
/**
* Returns all the messages exchanged
* in the supplied chat room having the given keywords
*
* @param room The chat room
* @param keywords keyword
* @param caseSensitive is keywords search case sensitive
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findByKeywords( ChatRoom room,
String[] keywords,
boolean caseSensitive)
throws RuntimeException
{
HashSet<EventObject> result = new HashSet<EventObject>();
try
{
// get the readers for this room
HistoryReader reader =
this.getHistoryForMultiChat(room).getReader();
// add the progress listeners
addHistorySearchProgressListeners(reader, 1);
Iterator<HistoryRecord> recs = reader.
findByKeywords(keywords, SEARCH_FIELD, caseSensitive);
while (recs.hasNext())
{
result
.add(convertHistoryRecordToMessageEvent(recs.next(), room));
}
removeHistorySearchProgressListeners(reader);
} catch (IOException e)
{
logger.error("Could not read history", e);
}
return result;
}
/**
* Returns the supplied number of recent messages exchanged
* in the supplied chat room
*
* @param room The chat room
* @param count messages count
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findLast(ChatRoom room, int count)
throws RuntimeException
{
LinkedList<EventObject> result = new LinkedList<EventObject>();
try
{
// get the readers for this room
HistoryReader reader =
this.getHistoryForMultiChat(room).getReader();
Iterator<HistoryRecord> recs = reader.findLast(count);
while (recs.hasNext())
{
result.add(
convertHistoryRecordToMessageEvent(recs.next(),
room));
}
}
catch (IOException e)
{
logger.error("Could not read history", e);
}
Collections.sort(result,
new ChatRoomMessageEventComparator<EventObject>());
int startIndex = result.size() - count;
if(startIndex < 0)
startIndex = 0;
return result.subList(startIndex, result.size());
}
/**
* Returns the supplied number of recent messages after the given date
* exchanged in the supplied chat room
*
* @param room The chat room
* @param date messages after date
* @param count messages count
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findFirstMessagesAfter( ChatRoom room,
Date date,
int count)
throws RuntimeException
{
LinkedList<EventObject> result = new LinkedList<EventObject>();
try
{
HistoryReader reader
= this.getHistoryForMultiChat(room).getReader();
Iterator<HistoryRecord> recs
= reader.findFirstRecordsAfter(date, count);
while (recs.hasNext())
{
result.add(
convertHistoryRecordToMessageEvent(recs.next(),
room));
}
}
catch (IOException e)
{
logger.error("Could not read history", e);
}
Collections.sort(result,
new ChatRoomMessageEventComparator<EventObject>());
int toIndex = count;
if(toIndex > result.size())
toIndex = result.size();
return result.subList(0, toIndex);
}
/**
* Returns the supplied number of recent messages before the given date
* exchanged in the supplied chat room
*
* @param room The chat room
* @param date messages before date
* @param count messages count
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
public Collection<EventObject> findLastMessagesBefore( ChatRoom room,
Date date,
int count)
throws RuntimeException
{
LinkedList<EventObject> result = new LinkedList<EventObject>();
try
{
HistoryReader reader =
this.getHistoryForMultiChat(room).getReader();
Iterator<HistoryRecord> recs
= reader.findLastRecordsBefore(date, count);
while (recs.hasNext())
{
result.add(
convertHistoryRecordToMessageEvent(recs.next(),
room));
}
}
catch (IOException e)
{
logger.error("Could not read history", e);
}
Collections.sort(result,
new ChatRoomMessageEventComparator<EventObject>());
int startIndex = result.size() - count;
if(startIndex < 0)
startIndex = 0;
return result.subList(startIndex, result.size());
}
/**
* A wrapper around HistorySearchProgressListener
* that fires events for MessageHistorySearchProgressListener
*/
private class SearchProgressWrapper
implements HistorySearchProgressListener
{
private MessageHistorySearchProgressListener listener = null;
double currentReaderProgressRatio = 0;
double accumulatedRatio = 0;
double currentProgress = 0;
double lastHistoryProgress = 0;
// used for more precise calculations with double values
int raiser = 1000;
SearchProgressWrapper(MessageHistorySearchProgressListener listener)
{
this.listener = listener;
}
private void setCurrentValues( HistoryReader currentReader,
int allRecords)
{
currentReaderProgressRatio =
(double)currentReader.countRecords()/allRecords * raiser;
accumulatedRatio += currentReaderProgressRatio;
}
public void progressChanged(ProgressEvent evt)
{
int progress = getProgressMapping(evt);
currentProgress = progress;
listener.progressChanged(
new net.java.sip.communicator.service.msghistory.event.
ProgressEvent(MessageHistoryServiceImpl.this,
evt, progress/raiser));
}
/**
* Calculates the progress according the count of the records
* we will search
* @param evt the progress event
* @return int
*/
private int getProgressMapping(ProgressEvent evt)
{
double tmpHistoryProgress =
currentReaderProgressRatio * evt.getProgress();
currentProgress += tmpHistoryProgress - lastHistoryProgress;
if(evt.getProgress()
== HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE)
{
lastHistoryProgress = 0;
// this is the last one and the last event fire the max
// there will be looses in currentProgress due to the devision
if((int)accumulatedRatio == raiser)
currentProgress = raiser *
MessageHistorySearchProgressListener
.PROGRESS_MAXIMUM_VALUE;
}
else
lastHistoryProgress = tmpHistoryProgress;
return (int)currentProgress;
}
/**
* clear the values
*/
void clear()
{
currentProgress = 0;
lastHistoryProgress = 0;
}
}
/**
* Simple message implementation.
*/
private static class MessageImpl
extends AbstractMessage
{
private final boolean isOutgoing;
private final Date messageReceivedDate;
private String msgSubType;
MessageImpl(String content, String contentType, String encoding,
String subject, String messageUID, boolean isOutgoing,
Date messageReceivedDate, String msgSubType)
{
super(content, contentType, encoding, subject, messageUID);
this.isOutgoing = isOutgoing;
this.messageReceivedDate = messageReceivedDate;
this.msgSubType = msgSubType;
}
public Date getMessageReceivedDate()
{
return messageReceivedDate;
}
public String getMsgSubType()
{
return msgSubType;
}
}
/**
* Used to compare MessageDeliveredEvent or MessageReceivedEvent
* and to be ordered in TreeSet according their timestamp
*/
private static class MessageEventComparator<T>
implements Comparator<T>
{
private final boolean reverseOrder;
MessageEventComparator(boolean reverseOrder)
{
this.reverseOrder = reverseOrder;
}
MessageEventComparator()
{
this(false);
}
public int compare(T o1, T o2)
{
Date date1;
Date date2;
if(o1 instanceof MessageDeliveredEvent)
date1 = ((MessageDeliveredEvent)o1).getTimestamp();
else if(o1 instanceof MessageReceivedEvent)
date1 = ((MessageReceivedEvent)o1).getTimestamp();
else if(o1 instanceof ChatRoomMessageDeliveredEvent)
date1 = ((ChatRoomMessageDeliveredEvent)o1).getTimestamp();
else if(o1 instanceof ChatRoomMessageReceivedEvent)
date1 = ((ChatRoomMessageReceivedEvent)o1).getTimestamp();
else
return 0;
if(o2 instanceof MessageDeliveredEvent)
date2 = ((MessageDeliveredEvent)o2).getTimestamp();
else if(o2 instanceof MessageReceivedEvent)
date2 = ((MessageReceivedEvent)o2).getTimestamp();
else if(o2 instanceof ChatRoomMessageDeliveredEvent)
date2 = ((ChatRoomMessageDeliveredEvent)o2).getTimestamp();
else if(o2 instanceof ChatRoomMessageReceivedEvent)
date2 = ((ChatRoomMessageReceivedEvent)o2).getTimestamp();
else
return 0;
if(reverseOrder)
return date2.compareTo(date1);
else
return date1.compareTo(date2);
}
}
/**
* Used to compare ChatRoomMessageDeliveredEvent
* or ChatRoomMessageReceivedEvent
* and to be ordered in TreeSet according their timestamp
*/
private static class ChatRoomMessageEventComparator<T>
implements Comparator<T>
{
public int compare(T o1, T o2)
{
Date date1;
Date date2;
if(o1 instanceof ChatRoomMessageDeliveredEvent)
date1 = ((ChatRoomMessageDeliveredEvent)o1).getTimestamp();
else if(o1 instanceof ChatRoomMessageReceivedEvent)
date1 = ((ChatRoomMessageReceivedEvent)o1).getTimestamp();
else
return 0;
if(o2 instanceof ChatRoomMessageDeliveredEvent)
date2 = ((ChatRoomMessageDeliveredEvent)o2).getTimestamp();
else if(o2 instanceof ChatRoomMessageReceivedEvent)
date2 = ((ChatRoomMessageReceivedEvent)o2).getTimestamp();
else
return 0;
return date1.compareTo(date2);
}
}
/**
* Simple ChatRoomMember implementation.
* Searches and for contact matches, to use its display name.
*/
static class ChatRoomMemberImpl
implements ChatRoomMember
{
private final ChatRoom chatRoom;
private final String name;
private ChatRoomMemberRole role;
private Contact contact = null;
private OperationSetPersistentPresence opsetPresence = null;
public ChatRoomMemberImpl(String name, ChatRoom chatRoom,
ChatRoomMemberRole role)
{
this.chatRoom = chatRoom;
this.name = name;
this.role = role;
}
public ChatRoom getChatRoom()
{
return chatRoom;
}
public ProtocolProviderService getProtocolProvider()
{
return chatRoom.getParentProvider();
}
public String getContactAddress()
{
return name;
}
public String getName()
{
String name = this.name;
if(getContact() != null
&& getContact().getDisplayName() != null)
{
name = getContact().getDisplayName();
}
return name;
}
public ChatRoomMemberRole getRole()
{
return role;
}
public byte[] getAvatar()
{
return null;
}
public void setRole(ChatRoomMemberRole newRole)
{
this.role = newRole;
}
public Contact getContact()
{
if(contact == null && opsetPresence == null)
{
opsetPresence = getProtocolProvider().getOperationSet(
OperationSetPersistentPresence.class);
if(opsetPresence != null)
{
contact
= opsetPresence.findContactByID(getContactAddress());
}
}
return contact;
}
public ConferenceDescription getConferenceDescription()
{
return null;
}
public void setConferenceDescription(ConferenceDescription cd)
{
return;
}
@Override
public PresenceStatus getPresenceStatus()
{
// FIXME is this correct response?
return GlobalStatusEnum.ONLINE;
}
}
/**
* Handles <tt>PropertyChangeEvent</tt> triggered from the modification of
* the isMessageHistoryEnabled property.
*/
private class MessageHistoryPropertyChangeListener
implements PropertyChangeListener
{
public void propertyChange(PropertyChangeEvent evt)
{
if(evt.getPropertyName()
.equals(MessageHistoryService.PNAME_IS_MESSAGE_HISTORY_ENABLED))
{
String newPropertyValue = (String) evt.getNewValue();
isHistoryLoggingEnabled
= new Boolean(newPropertyValue).booleanValue();
// If the message history is not enabled we stop here.
if (isHistoryLoggingEnabled)
loadMessageHistoryService();
else
stop(bundleContext);
}
else if(evt.getPropertyName().equals(
MessageHistoryService.PNAME_IS_RECENT_MESSAGES_DISABLED))
{
String newPropertyValue = (String) evt.getNewValue();
boolean isDisabled
= new Boolean(newPropertyValue).booleanValue();
if(isDisabled)
{
stopRecentMessages();
}
else if(isHistoryLoggingEnabled)
{
loadRecentMessages();
}
}
}
}
/**
* Loads the History and MessageHistoryService. Registers the service in the
* bundle context.
*/
private void loadMessageHistoryService()
{
configService.addPropertyChangeListener(
MessageHistoryService.PNAME_IS_RECENT_MESSAGES_DISABLED,
msgHistoryPropListener);
boolean isRecentMessagesDisabled = configService.getBoolean(
MessageHistoryService.PNAME_IS_RECENT_MESSAGES_DISABLED,
false);
if(!isRecentMessagesDisabled)
loadRecentMessages();
// start listening for newly register or removed protocol providers
bundleContext.addServiceListener(this);
for (ProtocolProviderService pps : getCurrentlyAvailableProviders())
{
this.handleProviderAdded(pps);
}
}
/**
* Returns currently registered in osgi ProtocolProviderServices.
* @return currently registered in osgi ProtocolProviderServices.
*/
List<ProtocolProviderService> getCurrentlyAvailableProviders()
{
List<ProtocolProviderService> res
= new ArrayList<ProtocolProviderService>();
ServiceReference[] protocolProviderRefs = null;
try
{
protocolProviderRefs = bundleContext.getServiceReferences(
ProtocolProviderService.class.getName(),
null);
}
catch (InvalidSyntaxException ex)
{
// this shouldn't happen since we're providing no parameter string
// but let's log just in case.
logger.error(
"Error while retrieving service refs", ex);
return res;
}
// in case we found any
if (protocolProviderRefs != null)
{
if (logger.isDebugEnabled())
logger.debug("Found "
+ protocolProviderRefs.length
+ " already installed providers.");
for (int i = 0; i < protocolProviderRefs.length; i++)
{
ProtocolProviderService provider
= (ProtocolProviderService) bundleContext
.getService(protocolProviderRefs[i]);
res.add(provider);
}
}
return res;
}
/**
* Stops the MessageHistoryService.
*/
private void stopMessageHistoryService()
{
// start listening for newly register or removed protocol providers
bundleContext.removeServiceListener(this);
ServiceReference[] protocolProviderRefs = null;
try
{
protocolProviderRefs = bundleContext.getServiceReferences(
ProtocolProviderService.class.getName(),
null);
}
catch (InvalidSyntaxException ex)
{
// this shouldn't happen since we're providing no parameter string
// but let's log just in case.
logger.error(
"Error while retrieving service refs", ex);
return;
}
// in case we found any
if (protocolProviderRefs != null)
{
for (int i = 0; i < protocolProviderRefs.length; i++)
{
ProtocolProviderService provider
= (ProtocolProviderService) bundleContext
.getService(protocolProviderRefs[i]);
this.handleProviderRemoved(provider);
}
}
}
public void messageDelivered(AdHocChatRoomMessageDeliveredEvent evt)
{
if(!isHistoryLoggingEnabled(
evt.getSourceAdHocChatRoom().getIdentifier()))
{
// logging is switched off for this particular chat room
return;
}
try
{
History history = this.
getHistoryForAdHocMultiChat(
evt.getSourceAdHocChatRoom());
writeMessage(
history, "out", evt.getMessage(), evt.getTimestamp(), false);
}
catch (IOException e)
{
logger.error("Could not add message to history", e);
}
}
public void messageDeliveryFailed(
AdHocChatRoomMessageDeliveryFailedEvent evt)
{
// nothing to do for the history service
}
public void messageReceived(AdHocChatRoomMessageReceivedEvent evt)
{
if(!isHistoryLoggingEnabled(
evt.getSourceChatRoom().getIdentifier()))
{
// logging is switched off for this particular chat room
return;
}
try
{
History history = this.getHistoryForAdHocMultiChat(
evt.getSourceChatRoom());
writeMessage(history, "in", evt.getSourceChatRoomParticipant(),
evt.getMessage(), evt.getTimestamp());
} catch (IOException e)
{
logger.error("Could not add message to history", e);
}
}
/**
* Called to notify interested parties that a change in our presence in
* an ad-hoc chat room has occurred. Changes may include us being join,
* left.
* @param evt the <tt>LocalUserAdHocChatRoomPresenceChangeEvent</tt>
* instance containing the ad-hoc chat room and the type, and reason of the
* change
*/
public void localUserAdHocPresenceChanged(
LocalUserAdHocChatRoomPresenceChangeEvent evt)
{
if(evt.getEventType()
== LocalUserAdHocChatRoomPresenceChangeEvent.LOCAL_USER_JOINED)
{
evt.getAdHocChatRoom().addMessageListener(this);
}
else
{
evt.getAdHocChatRoom().removeMessageListener(this);
}
}
/**
* Permanently removes all locally stored message history.
*
* @throws java.io.IOException
* Thrown if the history could not be removed due to a IO error.
*/
public void eraseLocallyStoredHistory()
throws IOException
{
HistoryID historyId = HistoryID.createFromRawID(
new String[] { "messages" });
historyService.purgeLocallyStoredHistory(historyId);
if(this.messageSourceService != null)
this.messageSourceService.eraseLocallyStoredHistory();
}
/**
* Permanently removes locally stored message history for the metacontact.
*
* @throws java.io.IOException
* Thrown if the history could not be removed due to a IO error.
*/
public void eraseLocallyStoredHistory(MetaContact contact)
throws IOException
{
Iterator<Contact> iter = contact.getContacts();
while (iter.hasNext())
{
Contact item = iter.next();
History history = this.getHistory(null, item);
historyService.purgeLocallyStoredHistory(history.getID());
}
if(this.messageSourceService != null)
this.messageSourceService.eraseLocallyStoredHistory(contact);
}
/**
* Permanently removes locally stored message history for the chatroom.
*
* @throws java.io.IOException
* Thrown if the history could not be removed due to a IO error.
*/
public void eraseLocallyStoredHistory(ChatRoom room)
throws IOException
{
History history = this.getHistoryForMultiChat(room);
historyService.purgeLocallyStoredHistory(history.getID());
if(this.messageSourceService != null)
this.messageSourceService.eraseLocallyStoredHistory(room);
}
/**
* Returns <code>true</code> if the "IS_MESSAGE_HISTORY_ENABLED"
* property is true, otherwise - returns <code>false</code>.
* Indicates to the user interface whether the history logging is enabled.
* @return <code>true</code> if the "IS_MESSAGE_HISTORY_ENABLED"
* property is true, otherwise - returns <code>false</code>.
*/
public boolean isHistoryLoggingEnabled()
{
return isHistoryLoggingEnabled;
}
/**
* Updates the "isHistoryLoggingEnabled" property through the
* <tt>ConfigurationService</tt>.
*
* @param isEnabled indicates if the history logging is
* enabled.
*/
public void setHistoryLoggingEnabled(boolean isEnabled)
{
isHistoryLoggingEnabled = isEnabled;
configService.setProperty(
MessageHistoryService.PNAME_IS_MESSAGE_HISTORY_ENABLED,
Boolean.toString(isHistoryLoggingEnabled));
}
/**
* Returns <code>true</code> if the "IS_MESSAGE_HISTORY_ENABLED"
* property is true for the <tt>id</tt>, otherwise - returns
* <code>false</code>.
* Indicates to the user interface whether the history logging is enabled
* for the supplied id (id for metacontact or for chat room).
* @return <code>true</code> if the "IS_MESSAGE_HISTORY_ENABLED"
* property is true for the <tt>id</tt>, otherwise - returns
* <code>false</code>.
*/
public boolean isHistoryLoggingEnabled(String id)
{
return configService.getBoolean(MessageHistoryService
.PNAME_IS_MESSAGE_HISTORY_PER_CONTACT_ENABLED_PREFIX
+ "." + id, true);
}
/**
* Updates the "isHistoryLoggingEnabled" property through the
* <tt>ConfigurationService</tt> for the contact.
*
* @param isEnabled indicates if the history logging is
* enabled for the contact.
*/
public void setHistoryLoggingEnabled(
boolean isEnabled, String id)
{
if(isEnabled)
configService.setProperty(
MessageHistoryService
.PNAME_IS_MESSAGE_HISTORY_PER_CONTACT_ENABLED_PREFIX
+ "." + id, null);
else
configService.setProperty(
MessageHistoryService
.PNAME_IS_MESSAGE_HISTORY_PER_CONTACT_ENABLED_PREFIX
+ "." + id, isEnabled);
}
}