Call History Impl and call history tests

cusax-fix
Damian Minkov 19 years ago
parent 90e516e1bb
commit 19ad943a80

@ -560,6 +560,7 @@
<target name="bundles"
depends="bundle-util,bundle-configuration,bundle-configuration-slick,
bundle-history,bundle-history-slick,bundle-messagehistory, bundle-msghistory-slick,
bundle-callhistory, bundle-callhistory-slick,
bundle-netaddr,bundle-netaddr-slick,bundle-slickless,
bundle-slick-runner,bundle-sip,bundle-sip-slick,bundle-fileaccess,
bundle-fileaccess-slick,bundle-media,bundle-media-slick,
@ -610,6 +611,27 @@
</jar>
</target>
<!--BUNDLE-CALLHISTORY-->
<target name="bundle-callhistory">
<jar compress="false" destfile="${bundles.dest}/callhistory.jar"
manifest="src/net/java/sip/communicator/impl/callhistory/callhistory.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/service/callhistory"
prefix="net/java/sip/communicator/service/callhistory"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/callhistory"
prefix="net/java/sip/communicator/impl/callhistory" />
</jar>
</target>
<!--BUNDLE-CALLEHISTORY-SLICK-->
<target name="bundle-callhistory-slick">
<jar compress="false" destfile="${bundles.dest}/callhistory-slick.jar"
manifest="test/net/java/sip/communicator/slick/callhistory/callhistory.slick.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/slick/callhistory"
prefix="net/java/sip/communicator/slick/callhistory"/>
</jar>
</target>
<!--BUNDLE-FILEACCESS-->
<target name="bundle-fileaccess">
<jar compress="false" destfile="${bundles.dest}/fileaccess.jar"

@ -54,7 +54,8 @@ oscar.auto.start.3= \
oscar.auto.start.4= \
file:sc-bundles/history.jar \
file:sc-bundles/msghistory.jar
file:sc-bundles/msghistory.jar \
file:sc-bundles/callhistory.jar
oscar.auto.start.66= \

@ -50,7 +50,8 @@ oscar.auto.start.4= \
file:sc-bundles/protocol-sip.jar \
file:sc-bundles/media.jar \
file:sc-bundles/meta-cl.jar \
file:sc-bundles/msghistory.jar
file:sc-bundles/msghistory.jar \
file:sc-bundles/callhistory.jar
oscar.auto.start.5= \
file:sc-bundles/slickless.jar \
@ -65,7 +66,8 @@ oscar.auto.start.5= \
file:sc-bundles/protocol-icq-slick.jar \
file:sc-bundles/protocol-sip-slick.jar \
file:sc-bundles/protocol-jabber-slick.jar \
file:sc-bundles/msghistory-slick.jar
file:sc-bundles/msghistory-slick.jar \
file:sc-bundles/callhistory-slick.jar
oscar.auto.start.100= \
file:sc-bundles/slick-runner.jar

@ -12,6 +12,7 @@ test.list=ConfigurationServiceLick \
IcqProtocolProviderSlick \
SipProtocolProviderServiceLick \
MsgHistoryServiceLick \
CallHistoryServiceLick \
JabberProtocolProviderSlick
# Set the name of the meta contact list file to use during testing so that

@ -0,0 +1,69 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.callhistory;
import org.osgi.framework.*;
import net.java.sip.communicator.service.history.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.service.callhistory.*;
/**
* Activates the CallHistoryService
*
* @author Damian Minkov
*/
public class CallHistoryActivator
implements BundleActivator
{
private static Logger logger =
Logger.getLogger(CallHistoryActivator.class);
private CallHistoryServiceImpl callHistoryService = null;
/**
* Initialize and start call history
*
* @param bundleContext BundleContext
* @throws Exception
*/
public void start(BundleContext bundleContext) throws Exception
{
try{
logger.logEntry();
ServiceReference refHistory = bundleContext.getServiceReference(
HistoryService.class.getName());
HistoryService historyService = (HistoryService)
bundleContext.getService(refHistory);
//Create and start the call history service.
callHistoryService =
new CallHistoryServiceImpl();
// set the configuration and history service
callHistoryService.setHistoryService(historyService);
callHistoryService.start(bundleContext);
bundleContext.registerService(
CallHistoryService.class.getName(), callHistoryService, null);
logger.info("Call History Service ...[REGISTERED]");
}
finally
{
logger.logExit();
}
}
public void stop(BundleContext bundleContext) throws Exception
{
}
}

@ -0,0 +1,900 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.callhistory;
import java.io.*;
import java.util.*;
import org.osgi.framework.*;
import net.java.sip.communicator.service.callhistory.*;
import net.java.sip.communicator.service.callhistory.event.*;
import net.java.sip.communicator.service.contactlist.*;
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.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* The Call History Service stores info about the calls made.
* Logs calls info for all protocol providers that support basic telephony
* (i.e. those that implement OperationSetBasicTelephony).
*
* @author Damian Minkov
*/
public class CallHistoryServiceImpl
implements CallHistoryService,
CallListener,
ServiceListener
{
/**
* The logger for this class.
*/
private static Logger logger = Logger
.getLogger(CallHistoryServiceImpl.class);
private static String[] STRUCTURE_NAMES =
new String[] { "callStart", "callEnd", "dir", "callParticipantIDs",
"callParticipantStart", "callParticipantEnd" };
private static HistoryRecordStructure recordStructure =
new HistoryRecordStructure(STRUCTURE_NAMES);
private static final String DELIM = ",";
/**
* 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 progressListeners = new Hashtable();
private Vector currentCallRecords = new Vector();
private HistoryCallChangeListener historyCallChangeListener
= new HistoryCallChangeListener();
public HistoryService getHistoryService()
{
return historyService;
}
/**
* Returns all the calls made by all the contacts
* in the supplied metacontact after the given date
*
* @param contact MetaContact
* @param startDate Date the start date of the calls
* @return Collection of CallRecords with CallParticipantRecord
* @throws RuntimeException
*/
public Collection findByStartDate(MetaContact contact, Date startDate)
throws RuntimeException
{
throw new UnsupportedOperationException("Not implemented yet!");
}
/**
* Returns all the calls made after the given date
*
* @param startDate Date the start date of the calls
* @return Collection of CallRecords with CallParticipantRecord
* @throws RuntimeException
*/
public Collection findByStartDate(Date startDate) throws RuntimeException
{
TreeSet result = new TreeSet(new CallRecordComparator());
try
{
// the default ones
History history = this.getHistory(null, null);
HistoryReader reader = history.getReader();
addHistorySearchProgressListeners(reader, 1);
QueryResultSet rs = reader.findByStartDate(startDate);
while (rs.hasNext())
{
HistoryRecord hr = (HistoryRecord) rs.next();
result.add(convertHistoryRecordToCallRecord(hr));
}
removeHistorySearchProgressListeners(reader);
}
catch (IOException ex)
{
logger.error("Could not read history", ex);
}
return result;
}
/**
* Returns all the calls made by all the contacts
* in the supplied metacontact before the given date
*
* @param contact MetaContact
* @param endDate Date the end date of the calls
* @return Collection of CallRecords with CallParticipantRecord
* @throws RuntimeException
*/
public Collection findByEndDate(MetaContact contact, Date endDate)
throws RuntimeException
{
throw new UnsupportedOperationException("Not implemented yet!");
}
/**
* Returns all the calls made before the given date
*
* @param endDate Date the end date of the calls
* @return Collection of CallRecords with CallParticipantRecord
* @throws RuntimeException
*/
public Collection findByEndDate(Date endDate) throws RuntimeException
{
TreeSet result = new TreeSet(new CallRecordComparator());
try
{
// the default ones
History history = this.getHistory(null, null);
HistoryReader reader = history.getReader();
addHistorySearchProgressListeners(reader, 1);
QueryResultSet rs = reader.findByEndDate(endDate);
while (rs.hasNext())
{
HistoryRecord hr = (HistoryRecord) rs.next();
result.add(convertHistoryRecordToCallRecord(hr));
}
removeHistorySearchProgressListeners(reader);
}
catch (IOException ex)
{
logger.error("Could not read history", ex);
}
return result;
}
/**
* Returns all the calls made by all the contacts
* in the supplied metacontact between the given dates
*
* @param contact MetaContact
* @param startDate Date the start date of the calls
* @param endDate Date the end date of the conversations
* @return Collection of CallRecords with CallParticipantRecord
* @throws RuntimeException
*/
public Collection findByPeriod(MetaContact contact, Date startDate, Date endDate)
throws RuntimeException
{
throw new UnsupportedOperationException("Not implemented yet!");
}
/**
* Returns all the calls made between the given dates
*
* @param startDate Date the start date of the calls
* @param endDate Date the end date of the conversations
* @return Collection of CallRecords with CallParticipantRecord
* @throws RuntimeException
*/
public Collection findByPeriod(Date startDate, Date endDate) throws
RuntimeException
{
TreeSet result = new TreeSet(new CallRecordComparator());
try
{
// the default ones
History history = this.getHistory(null, null);
HistoryReader reader = history.getReader();
addHistorySearchProgressListeners(reader, 1);
QueryResultSet rs = reader.findByPeriod(startDate, endDate);
while (rs.hasNext())
{
HistoryRecord hr = (HistoryRecord) rs.next();
result.add(convertHistoryRecordToCallRecord(hr));
}
removeHistorySearchProgressListeners(reader);
}
catch (IOException ex)
{
logger.error("Could not read history", ex);
}
return result;
}
/**
* Returns the supplied number of calls by all the contacts
* in the supplied metacontact
*
* @param contact MetaContact
* @param count calls count
* @return Collection of CallRecords with CallParticipantRecord
* @throws RuntimeException
*/
public Collection findLast(MetaContact contact, int count)
throws RuntimeException
{
throw new UnsupportedOperationException("Not implemented yet!");
}
/**
* Returns the supplied number of calls made
*
* @param count calls count
* @return Collection of CallRecords with CallParticipantRecord
* @throws RuntimeException
*/
public Collection findLast(int count) throws RuntimeException
{
TreeSet result = new TreeSet(new CallRecordComparator());
try
{
// the default ones
History history = this.getHistory(null, null);
QueryResultSet rs = history.getReader().findLast(count);
while (rs.hasNext())
{
HistoryRecord hr = (HistoryRecord) rs.next();
result.add(convertHistoryRecordToCallRecord(hr));
}
}
catch (IOException ex)
{
logger.error("Could not read history", ex);
}
return result;
}
/**
* 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 {
History retVal = null;
String localId = localContact == null ? "default" : localContact
.getAddress();
String remoteId = remoteContact == null ? "default" : remoteContact
.getAddress();
HistoryID historyId = HistoryID.createFromID(new String[] { "callhistory",
localId, remoteId });
if (this.historyService.isHistoryExisting(historyId))
{
retVal = this.historyService.getHistory(historyId);
} else {
retVal = this.historyService.createHistory(historyId,
recordStructure);
}
return retVal;
}
/**
* Used to convert HistoryRecord in CallReord and CallParticipantRecord
* which are returned by the finder methods
*
* @param hr HistoryRecord
* @return Object CallRecord
*/
private Object convertHistoryRecordToCallRecord(HistoryRecord hr)
{
CallRecord result = new CallRecord();
LinkedList callParticipantIDs = null;
LinkedList callParticipantStart = null;
LinkedList callParticipantEnd = null;
// History structure
// 0 - callStart
// 1 - callEnd
// 2 - dir
// 3 - callParticipantIDs
// 4 - callParticipantStart
// 5 - callParticipantEnd
for (int i = 0; i < hr.getPropertyNames().length; i++)
{
String propName = hr.getPropertyNames()[i];
String value = hr.getPropertyValues()[i];
if(propName.equals(STRUCTURE_NAMES[0]))
result.setStartTime(new Date(Long.parseLong(value)));
else if(propName.equals(STRUCTURE_NAMES[1]))
result.setEndTime(new Date(Long.parseLong(value)));
else if(propName.equals(STRUCTURE_NAMES[2]))
result.setDirection(value);
else if(propName.equals(STRUCTURE_NAMES[3]))
callParticipantIDs = getCSVs(value);
else if(propName.equals(STRUCTURE_NAMES[4]))
callParticipantStart = getCSVs(value);
else if(propName.equals(STRUCTURE_NAMES[5]))
callParticipantEnd = getCSVs(value);
}
for (int i = 0; i < callParticipantIDs.size(); i++)
{
CallParticipantRecord cpr = new CallParticipantRecord(
(String)callParticipantIDs.get(i),
new Date(Long.parseLong((String)callParticipantStart.get(i))),
new Date(Long.parseLong((String)callParticipantEnd.get(i)))
);
result.getParticipantRecords().add(cpr);
}
return result;
}
/**
* Returns list of String items contained in the supplied string
* separated by DELIM
* @param str String
* @return LinkedList
*/
private LinkedList getCSVs(String str)
{
LinkedList result = new LinkedList();
StringTokenizer toks = new StringTokenizer(str, DELIM);
while(toks.hasMoreTokens())
{
result.add(toks.nextToken());
}
return result;
}
/**
* starts the service. Check the current registerd protocol providers
* which supports BasicTelephony and adds calls listener to them
*
* @param bc BundleContext
*/
public void start(BundleContext bc)
{
logger.debug("Starting the call history implementation.");
this.bundleContext = bc;
// start listening for newly register or removed protocol providers
bc.addServiceListener(this);
ServiceReference[] protocolProviderRefs = null;
try
{
protocolProviderRefs = bc.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)
{
logger.debug("Found "
+ protocolProviderRefs.length
+ " already installed providers.");
for (int i = 0; i < protocolProviderRefs.length; i++)
{
ProtocolProviderService provider = (ProtocolProviderService) bc
.getService(protocolProviderRefs[i]);
this.handleProviderAdded(provider);
}
}
}
/**
* Writes the given record to the history service
* @param callRecord CallRecord
* @param source Contact
* @param destination Contact
*/
private void writeCall(CallRecord callRecord, Contact source,
Contact destination)
{
try {
History history = this.getHistory(source, destination);
HistoryWriter historyWriter = history.getWriter();
StringBuffer callParticipantIDs = new StringBuffer();
StringBuffer callParticipantStartTime = new StringBuffer();
StringBuffer callParticipantEndTime = new StringBuffer();
Iterator iter = callRecord.getParticipantRecords().iterator();
while (iter.hasNext())
{
if(callParticipantIDs.length() > 0)
{
callParticipantIDs.append(DELIM);
callParticipantStartTime.append(DELIM);
callParticipantEndTime.append(DELIM);
}
CallParticipantRecord item = (CallParticipantRecord) iter.next();
callParticipantIDs.append(item.getParticipantAddress());
callParticipantStartTime.append(String.valueOf(item.getStartTime().getTime()));
callParticipantEndTime.append(String.valueOf(item.getEndTime().getTime()));
}
historyWriter.addRecord(new String[] {
String.valueOf(callRecord.getStartTime().getTime()),
String.valueOf(callRecord.getEndTime().getTime()),
callRecord.getDirection(),
callParticipantIDs.toString(),
callParticipantStartTime.toString(),
callParticipantEndTime.toString()},
new Date()); // this date is when the history record is written
} catch (IOException e)
{
logger.error("Could not add call 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;
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;
logger.debug("History service unregistered.");
}
}
}
/**
* When new protocol provider is registered we check
* does it supports BasicTelephony and if so add a listener to it
*
* @param serviceEvent ServiceEvent
*/
public void serviceChanged(ServiceEvent serviceEvent)
{
Object sService = bundleContext.getService(serviceEvent.getServiceReference());
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;
}
logger.debug("Service is a protocol provider.");
if (serviceEvent.getType() == ServiceEvent.REGISTERED)
{
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 Call History Service to existing or
* just registered protocol provider. Checks if the provider has implementation
* of OperationSetBasicTelephony
*
* @param provider ProtocolProviderService
*/
private void handleProviderAdded(ProtocolProviderService provider)
{
logger.debug("Adding protocol provider " + provider.getProtocolName());
// check whether the provider has a basic telephony operation set
OperationSetBasicTelephony opSetTelephony
= (OperationSetBasicTelephony) provider
.getSupportedOperationSets().get(
OperationSetBasicTelephony.class.getName());
if (opSetTelephony != null)
{
opSetTelephony.addCallListener(this);
}
else
{
logger.trace("Service did not have a basic telephony op. set.");
}
}
/**
* Removes the specified provider from the list of currently known providers
* and ignores all the calls made by it
*
* @param provider the ProtocolProviderService that has been unregistered.
*/
private void handleProviderRemoved(ProtocolProviderService provider)
{
OperationSetBasicTelephony opSetTelephony
= (OperationSetBasicTelephony) provider
.getSupportedOperationSets().get(
OperationSetBasicTelephony.class.getName());
if (opSetTelephony != null)
{
opSetTelephony.removeCallListener(this);
}
}
/**
* Adding progress listener for monitoring progress of search process
*
* @param listener HistorySearchProgressListener
*/
public void addSearchProgressListener(CallHistorySearchProgressListener
listener)
{
synchronized(progressListeners){
HistorySearchProgressListener wrapperListener =
new SearchProgressWrapper(listener);
progressListeners.put(listener, wrapperListener);
}
}
/**
* Removing progress listener
*
* @param listener HistorySearchProgressListener
*/
public void removeSearchProgressListener(
CallHistorySearchProgressListener listener)
{
synchronized(progressListeners){
progressListeners.remove(listener);
}
}
/**
* Add the registered CallHistorySearchProgressListeners
* to the given HistoryReader
*
* @param reader HistoryReader
* @param countContacts number of contacts will search
*/
private void addHistorySearchProgressListeners(
HistoryReader reader, int countContacts)
{
synchronized(progressListeners)
{
Iterator iter = progressListeners.values().iterator();
while (iter.hasNext())
{
SearchProgressWrapper l =
(SearchProgressWrapper) iter.next();
l.contactCount = countContacts;
reader.addSearchProgressListener(l);
}
}
}
/**
* Removes the registered CallHistorySearchProgressListeners
* from the given HistoryReader
*
* @param reader HistoryReader
*/
private void removeHistorySearchProgressListeners(HistoryReader reader)
{
synchronized(progressListeners)
{
Iterator iter = progressListeners.values().iterator();
while (iter.hasNext())
{
SearchProgressWrapper l =
(SearchProgressWrapper) iter.next();
l.clear();
reader.removeSearchProgressListener(l);
}
}
}
/**
* Gets all the history readers for the contacts in the given MetaContact
* @param contact MetaContact
* @return Hashtable
*/
private Hashtable getHistoryReaders(MetaContact contact)
{
Hashtable readers = new Hashtable();
Iterator iter = contact.getContacts();
while (iter.hasNext())
{
Contact item = (Contact) 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;
}
/**
* CallListener implementation for incoming calls
* @param event CallEvent
*/
public void incomingCallReceived(CallEvent event)
{
handleNewCall(event.getSourceCall(), CallRecord.IN);
}
/**
* CallListener implementation for outgoing calls
* @param event CallEvent
*/
public void outgoingCallCreated(CallEvent event)
{
logger.info("outgoingCallCreated");
handleNewCall(event.getSourceCall(), CallRecord.OUT);
}
/**
* CallListener implementation for call endings
* @param event CallEvent
*/
public void callEnded(CallEvent event)
{
logger.info("callEnded");
Date endTime = new Date();
CallRecord callRecord = findCallRecord(event.getSourceCall());
// no such call
if (callRecord == null)
return;
callRecord.setEndTime(endTime);
writeCall(callRecord, null, null);
currentCallRecords.remove(callRecord);
}
/**
* Adding a record for joining participant
* @param callParticipant CallParticipant
*/
private void handleParticipantAdded(CallParticipant callParticipant)
{
CallRecord callRecord = findCallRecord(callParticipant.getCall());
// no such call
if(callRecord == null)
return;
CallParticipantRecord newRec = new CallParticipantRecord(
callParticipant.getAddress(),
new Date(),
null);
callRecord.getParticipantRecords().add(newRec);
}
/**
* Adding a record for removing participant from call
* @param callParticipant CallParticipant
*/
private void handleParticipantRemoved(CallParticipant callParticipant)
{
logger.info("handleParticipantRemoved");
CallRecord callRecord = findCallRecord(callParticipant.getCall());
String pAddress = callParticipant.getAddress();
CallParticipantRecord cpRecord =
callRecord.findParticipantRecord(pAddress);
// no such participant
if(cpRecord == null)
return;
cpRecord.setEndTime(new Date());
}
/**
* Finding a CallRecord for the given call
* @param call Call
* @return CallRecord
*/
private CallRecord findCallRecord(Call call)
{
Iterator iter = currentCallRecords.iterator();
while (iter.hasNext())
{
CallRecord item = (CallRecord) iter.next();
if(item.getSourceCall().equals(call))
return item;
}
return null;
}
/**
* Adding a record for a new call
* @param sourceCall Call
* @param direction String
*/
private void handleNewCall(Call sourceCall, String direction)
{
// if call exist. its not new
if(currentCallRecords.contains(sourceCall))
return;
CallRecord newRecord = new CallRecord(
sourceCall,
direction,
new Date(),
null);
sourceCall.addCallChangeListener(historyCallChangeListener);
currentCallRecords.add(newRecord);
// if has already perticipants Dispatch them
Iterator iter = sourceCall.getCallParticipants();
while (iter.hasNext())
{
CallParticipant item = (CallParticipant) iter.next();
handleParticipantAdded(item);
}
}
/**
* A wrapper around HistorySearchProgressListener
* that fires events for CallHistorySearchProgressListener
*/
private class SearchProgressWrapper
implements HistorySearchProgressListener
{
private CallHistorySearchProgressListener listener = null;
int contactCount = 0;
int currentContactCount = 0;
int currentProgress = 0;
int lastHistoryProgress = 0;
SearchProgressWrapper(CallHistorySearchProgressListener listener)
{
this.listener = listener;
}
public void progressChanged(ProgressEvent evt)
{
int progress = getProgressMapping(evt.getProgress());
listener.progressChanged(
new net.java.sip.communicator.service.callhistory.event.
ProgressEvent(CallHistoryServiceImpl.this, evt, progress));
}
/**
* Calculates the progress according the count of the contacts
* we will search
* @param historyProgress int
* @return int
*/
private int getProgressMapping(int historyProgress)
{
currentProgress += (historyProgress - lastHistoryProgress)/contactCount;
if(historyProgress == HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE)
{
currentContactCount++;
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(currentContactCount == contactCount)
currentProgress =
CallHistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE;
}
else
lastHistoryProgress = historyProgress;
return currentProgress;
}
/**
* clear the values
*/
void clear()
{
contactCount = 0;
currentProgress = 0;
lastHistoryProgress = 0;
currentContactCount = 0;
}
}
/**
* Used to compare CallRecords
* and to be ordered in TreeSet according their timestamp
*/
private class CallRecordComparator
implements Comparator
{
public int compare(Object o1, Object o2)
{
return ((CallRecord)o1).getStartTime().
compareTo(((CallRecord)o2).getStartTime());
}
}
/**
* Receive events for adding or removing participants from a call
*/
private class HistoryCallChangeListener
implements CallChangeListener
{
public void callParticipantAdded(CallParticipantEvent evt)
{
handleParticipantAdded(evt.getSourceCallParticipant());
}
public void callParticipantRemoved(CallParticipantEvent evt)
{
handleParticipantRemoved(evt.getSourceCallParticipant());
}
public void callStateChanged(CallChangeEvent evt)
{
}
}
}

@ -0,0 +1,19 @@
Bundle-Activator: net.java.sip.communicator.impl.callhistory.CallHistoryActivator
Bundle-Name: Call History Service Provider
Bundle-Description: A bundle that implements the call history package.
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
Import-Package: org.ungoverned.gravity.servicebinder,
org.osgi.framework,
net.java.sip.communicator.service.fileaccess,
net.java.sip.communicator.service.history,
net.java.sip.communicator.service.history.event,
net.java.sip.communicator.service.contactlist,
net.java.sip.communicator.service.history.records,
net.java.sip.communicator.util,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.icqconstants,
net.java.sip.communicator.service.protocol.event,
Export-Package: net.java.sip.communicator.service.callhistory,
net.java.sip.communicator.service.callhistory.event
Metadata-Location: /net/java/sip/communicator/impl/msghistory/callhistory.metadata.xml

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<bundle>
<component class="net.java.sip.communicator.impl.msghistory.MessageHistoryServiceImpl">
<provides service="net.java.sip.communicator.service.msghistory.MessageHistoryService"/>
<requires service="net.java.sip.communicator.service.configuration.ConfigurationService"
filter=""
policy="static"
cardinality="1..1"
bind-method="setConfigurationService"
unbind-method="unsetConfigurationService" />
<requires service="net.java.sip.communicator.service.history.HistoryService"
filter=""
policy="static"
cardinality="1..1"
bind-method="setHistoryService"
unbind-method="unsetHistoryService" />
<requires service="net.java.sip.communicator.service.protocol.ProtocolProviderService"
filter=""
cardinality="1..n"
policy="dynamic"
bind-method="addProtocolProvider"
unbind-method="removeProtocolProvider" />
</component>
</bundle>

@ -0,0 +1,161 @@
package net.java.sip.communicator.impl.protocol.mock;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.impl.protocol.sip.CallParticipantSipImpl;
import net.java.sip.communicator.util.Logger;
/**
* @author Damian Minkov
*/
public class MockCall
extends Call
implements CallParticipantListener
{
private static final Logger logger = Logger.getLogger(MockCall.class);
/**
* A list containing all <tt>CallParticipant</tt>s of this call.
*/
private Vector callParticipants = new Vector();
/**
* The state that this call is currently in.
*/
private CallState callState = CallState.CALL_INITIALIZATION;
public MockCall(MockProvider sourceProvider)
{
super(sourceProvider);
}
/**
* Returns an iterator over all call participants.
*
* @return an Iterator over all participants currently involved in the
* call.
*/
public Iterator getCallParticipants()
{
return callParticipants.iterator();
}
/**
* Returns the number of participants currently associated with this call.
*
* @return an <tt>int</tt> indicating the number of participants
* currently associated with this call.
*/
public int getCallParticipantsCount()
{
return callParticipants.size();
}
/**
* Returns the state that this call is currently in.
*
* @return a reference to the <tt>CallState</tt> instance that the call
* is currently in.
*/
public CallState getCallState()
{
return callState;
}
/**
* Adds <tt>callParticipant</tt> to the list of participants in this call.
* If the call participant is already included in the call, the method has
* no effect.
*
* @param callParticipant the new <tt>CallParticipant</tt>
*/
public void addCallParticipant(MockCallParticipant callParticipant)
{
if(callParticipants.contains(callParticipant))
return;
callParticipant.addCallParticipantListener(this);
this.callParticipants.add(callParticipant);
logger.info("Will fire participant added");
fireCallParticipantEvent(
callParticipant, CallParticipantEvent.CALL_PARTICIPANT_ADDED);
}
/**
* Removes <tt>callParticipant</tt> from the list of participants in this
* call. The method has no effect if there was no such participant in the
* call.
*
* @param callParticipant the <tt>CallParticipant</tt> leaving the call;
*/
public void removeCallParticipant(MockCallParticipant callParticipant)
{
if(!callParticipants.contains(callParticipant))
return;
this.callParticipants.remove(callParticipant);
callParticipant.removeCallParticipantListener(this);
fireCallParticipantEvent(
callParticipant, CallParticipantEvent.CALL_PARTICIPANT_REMVOVED);
if(callParticipants.size() == 0)
setCallState(CallState.CALL_ENDED);
}
/**
* Sets the state of this call and fires a call change event notifying
* registered listenres for the change.
*
* @param newState a reference to the <tt>CallState</tt> instance that
* the call is to enter.
*/
public void setCallState(CallState newState)
{
CallState oldState = getCallState();
if(oldState == newState)
return;
this.callState = newState;
fireCallChangeEvent(
CallChangeEvent.CALL_STATE_CHANGE, oldState, newState);
}
public void participantStateChanged(CallParticipantChangeEvent evt)
{
if ( ( (CallParticipantState) evt.getNewValue())
== CallParticipantState.DISCONNECTED
|| ( (CallParticipantState) evt.getNewValue())
== CallParticipantState.FAILED)
{
removeCallParticipant(
(MockCallParticipant) evt.getSourceCallParticipant());
}
else if ( ( (CallParticipantState) evt.getNewValue())
== CallParticipantState.CONNECTED
&& getCallState().equals(CallState.CALL_INITIALIZATION))
{
setCallState(CallState.CALL_IN_PROGRESS);
}
}
public void participantDisplayNameChanged(CallParticipantChangeEvent evt)
{
}
public void participantAddressChanged(CallParticipantChangeEvent evt)
{
}
public void participantImageChanged(CallParticipantChangeEvent evt)
{
}
}

@ -0,0 +1,160 @@
package net.java.sip.communicator.impl.protocol.mock;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.CallParticipantChangeEvent;
/**
* <p> </p>
*
* <p> </p>
*
* <p> </p>
*
* <p> </p>
*
* @author Damian Minkov
*/
public class MockCallParticipant
extends AbstractCallParticipant
{
/**
* The sip address of this participant
*/
private String participantAddress = null;
/**
* The call participant belongs to.
*/
private MockCall call;
/**
* A string uniquely identifying the participant.
*/
private String participantID;
/**
* Indicates the date when is call participant passed into its current state.
*/
protected Date currentStateStartDate = new Date();
/**
* The state of the call participant.
*/
protected CallParticipantState callParticipantState =
CallParticipantState.UNKNOWN;
public MockCallParticipant(String address, MockCall owningCall)
{
this.participantAddress = address;
this.call = owningCall;
call.addCallParticipant(this);
//create the uid
this.participantID = String.valueOf( System.currentTimeMillis())
+ String.valueOf(hashCode());
}
/**
* Returns a String locator for that participant.
*
* @return the participant's address or phone number.
*/
public String getAddress()
{
return participantAddress;
}
/**
* Returns a reference to the call that this participant belongs to.
*
* @return a reference to the call containing this participant.
*/
public Call getCall()
{
return call;
}
/**
* Returns the date (time) when this call participant acquired its
* current status.
*
* @return a java.util.Date object containing the date when this call
* participant entered its current state.
*/
public Date getCurrentStateStartDate()
{
return currentStateStartDate;
}
/**
* Returns a human readable name representing this participant.
*
* @return a String containing a name for that participant.
*/
public String getDisplayName()
{
return participantAddress;
}
/**
* The method returns an image representation of the call participant
* (e.g.
*
* @return byte[] a byte array containing the image or null if no image
* is available.
*/
public byte[] getImage()
{
return null;
}
/**
* Returns a unique identifier representing this participant.
*
* @return an identifier representing this call participant.
*/
public String getParticipantID()
{
return participantID;
}
/**
* Returns an object representing the current state of that participant.
*
* @return a CallParticipantState instance representin the participant's
* state.
*/
public CallParticipantState getState()
{
return callParticipantState;
}
/**
* Causes this CallParticipant to enter the specified state. The method also
* sets the currentStateStartDate field and fires a
* CallParticipantChangeEvent.
*
* @param newState the state this call participant should enter.
* @param reason a string that could be set to contain a human readable
* explanation for the transition (particularly handy when moving into a
* FAILED state).
*/
protected void setState(CallParticipantState newState, String reason)
{
CallParticipantState oldState = getState();
if(oldState == newState)
return;
this.callParticipantState = newState;
this.currentStateStartDate = new Date();
fireCallParticipantChangeEvent(
CallParticipantChangeEvent.CALL_PARTICIPANT_STATE_CHANGE,
oldState,
newState);
}
}

@ -0,0 +1,284 @@
package net.java.sip.communicator.impl.protocol.mock;
import java.text.*;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* A mock implementation of a basic telephony opearation set
*
* @author Damian Minkov
*/
public class MockOperationSetBasicTelephony
implements OperationSetBasicTelephony,
CallChangeListener
{
private static final Logger logger
= Logger.getLogger(MockOperationSetBasicTelephony.class);
/**
* A list of listeners registered for
* <tt>CallEvent</tt>s.
*/
private Vector callListeners = new Vector();
/**
* A reference to the <tt>ProtocolProviderServiceSipImpl</tt> instance
* that created us.
*/
private MockProvider protocolProvider = null;
/**
* A table mapping call ids against call instances.
*/
private Hashtable activeCalls = new Hashtable();
public MockOperationSetBasicTelephony(MockProvider protocolProvider)
{
this.protocolProvider = protocolProvider;
}
/**
* Registers the specified CallListener with this provider so that it
* could be notified when incoming calls are received.
*
* @param listener the listener to register with this provider.
*/
public void addCallListener(CallListener listener)
{
synchronized(callListeners){
callListeners.add(listener);
}
}
/**
* Indicates a user request to answer an incoming call from the specified
* CallParticipant.
*
* @param participant the call participant that we'd like to anwer.
* @throws OperationFailedException with the corresponding code if we
* encounter an error while performing this operation.
*/
public void answerCallParticipant(CallParticipant participant) throws
OperationFailedException
{
MockCallParticipant callParticipant
= (MockCallParticipant)participant;
if(participant.getState().equals(CallParticipantState.CONNECTED))
{
logger.info("Ignoring user request to answer a CallParticipant "
+ "that is already connected. CP:" + participant);
return;
}
callParticipant.setState(CallParticipantState.CONNECTED, null);
}
/**
* Create a new call and invite the specified CallParticipant to it.
*
* @param uri the address of the callee that we should invite to a new
* call.
* @return CallParticipant the CallParticipant that will represented by
* the specified uri. All following state change events will be
* delivered through that call participant. The Call that this
* participant is a member of could be retrieved from the
* CallParticipatn instance with the use of the corresponding method.
* @throws OperationFailedException with the corresponding code if we
* fail to create the call.
* @throws ParseException if <tt>callee</tt> is not a valid sip address
* string.
*/
public Call createCall(String uri) throws OperationFailedException,
ParseException
{
return createNewCall(uri);
}
/**
* Create a new call and invite the specified CallParticipant to it.
*
* @param callee the address of the callee that we should invite to a
* new call.
* @return CallParticipant the CallParticipant that will represented by
* the specified uri. All following state change events will be
* delivered through that call participant. The Call that this
* participant is a member of could be retrieved from the
* CallParticipatn instance with the use of the corresponding method.
* @throws OperationFailedException with the corresponding code if we
* fail to create the call.
*/
public Call createCall(Contact callee) throws OperationFailedException
{
return createNewCall(callee.getAddress());
}
private Call createNewCall(String address)
{
MockCall newCall = new MockCall(protocolProvider);
newCall.addCallChangeListener(this);
activeCalls.put(newCall.getCallID(), newCall);
new MockCallParticipant(address, newCall);
return newCall;
}
/**
* Returns an iterator over all currently active calls.
*
* @return Iterator
*/
public Iterator getActiveCalls()
{
return activeCalls.values().iterator();
}
/**
* Indicates a user request to end a call with the specified call
* particiapnt.
*
* @param participant the participant that we'd like to hang up on.
* @throws OperationFailedException with the corresponding code if we
* encounter an error while performing this operation.
*/
public void hangupCallParticipant(CallParticipant participant) throws
OperationFailedException
{
//do nothing if the call is already ended
if (participant.getState().equals(CallParticipantState.DISCONNECTED))
{
logger.debug("Ignoring a request to hangup a call participant "
+"that is already DISCONNECTED");
return;
}
MockCallParticipant callParticipant
= (MockCallParticipant)participant;
logger.info("hangupCallParticipant");
callParticipant.setState(CallParticipantState.DISCONNECTED, null);
}
/**
* Resumes communication with a call participant previously put on hold.
*
* @param participant the call participant to put on hold.
* @todo Implement this
* net.java.sip.communicator.service.protocol.OperationSetBasicTelephony
* method
*/
public void putOffHold(CallParticipant participant)
{
}
/**
* Puts the specified CallParticipant "on hold".
*
* @param participant the participant that we'd like to put on hold.
* @throws OperationFailedException with the corresponding code if we
* encounter an error while performing this operation.
* @todo Implement this
* net.java.sip.communicator.service.protocol.OperationSetBasicTelephony
* method
*/
public void putOnHold(CallParticipant participant) throws
OperationFailedException
{
}
/**
* Removes the specified listener from the list of call listeners.
*
* @param listener the listener to unregister.
*/
public void removeCallListener(CallListener listener)
{
synchronized(callListeners){
callListeners.remove(listener);
}
}
public Call receiveCall(String fromAddress)
throws Exception
{
Call newCall = createCall(fromAddress);
fireCallEvent(CallEvent.CALL_RECEIVED, newCall);
return newCall;
}
public Call placeCall(String toAddress)
throws Exception
{
Call newCall = createCall(toAddress);
fireCallEvent(CallEvent.CALL_INITIATED, newCall);
return newCall;
}
/**
* Creates and dispatches a <tt>CallEvent</tt> notifying registered
* listeners that an event with id <tt>eventID</tt> has occurred on
* <tt>sourceCall</tt>.
*
* @param eventID the ID of the event to dispatch
* @param sourceCall the call on which the event has occurred.
*/
protected void fireCallEvent( int eventID,
Call sourceCall)
{
CallEvent cEvent = new CallEvent(sourceCall, eventID);
logger.debug("Dispatching a CallEvent to "
+ callListeners.size()
+" listeners. event is: " + cEvent.toString());
Iterator listeners = new ArrayList(callListeners).iterator();
while(listeners.hasNext())
{
CallListener listener = (CallListener)listeners.next();
if(eventID == CallEvent.CALL_INITIATED)
listener.outgoingCallCreated(cEvent);
else if(eventID == CallEvent.CALL_RECEIVED)
listener.incomingCallReceived(cEvent);
else if(eventID == CallEvent.CALL_ENDED)
listener.callEnded(cEvent);
}
}
public CallParticipant addNewCallParticipant(Call call, String address)
{
return new MockCallParticipant(address, (MockCall)call);
}
public void callParticipantAdded(CallParticipantEvent evt)
{
}
public void callParticipantRemoved(CallParticipantEvent evt)
{
}
public void callStateChanged(CallChangeEvent evt)
{
if(evt.getEventType().equals(CallChangeEvent.CALL_STATE_CHANGE)
&& ((CallState)evt.getNewValue()).equals(CallState.CALL_ENDED))
{
MockCall sourceCall = (MockCall)this.activeCalls
.remove(evt.getSourceCall().getCallID());
logger.trace( "Removing call " + sourceCall + " from the list of "
+ "active calls because it entered an ENDED state");
fireCallEvent(CallEvent.CALL_ENDED, sourceCall);
}
}
}

@ -9,6 +9,7 @@
import java.util.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.callhistory.event.*;
/**
* The Call History Service stores info about calls made from various protocols
@ -55,6 +56,38 @@ Collection findByEndDate(MetaContact contact, Date endDate)
Collection findByPeriod(MetaContact contact, Date startDate, Date endDate)
throws RuntimeException;
/**
* Returns all the calls made after the given date
*
* @param startDate Date the start date of the calls
* @return Collection of CallReceivedEvent
* @throws RuntimeException
*/
Collection findByStartDate(Date startDate)
throws RuntimeException;
/**
* Returns all the calls made before the given date
*
* @param endDate Date the end date of the calls
* @return Collection of CallReceivedEvent
* @throws RuntimeException
*/
Collection findByEndDate(Date endDate)
throws RuntimeException;
/**
* Returns all the calls made between the given dates
*
* @param startDate Date the start date of the calls
* @param endDate Date the end date of the calls
* @return Collection of CallReceivedEvent
* @throws RuntimeException
*/
Collection findByPeriod(Date startDate, Date endDate)
throws RuntimeException;
/**
* Returns the supplied number of recent calls made by all the contacts
* in the supplied metacontact
@ -67,4 +100,29 @@ Collection findByPeriod(MetaContact contact, Date startDate, Date endDate)
Collection findLast(MetaContact contact, int count)
throws RuntimeException;
/**
* Returns the supplied number of recent calls made by all the contacts
* in the supplied metacontact
*
* @param count calls count
* @return Collection of CallReceivedEvent
* @throws RuntimeException
*/
Collection findLast(int count)
throws RuntimeException;
/**
* Adding progress listener for monitoring progress of search process
*
* @param listener HistorySearchProgressListener
*/
void addSearchProgressListener(CallHistorySearchProgressListener listener);
/**
* Removing progress listener
*
* @param listener HistorySearchProgressListener
*/
void removeSearchProgressListener(CallHistorySearchProgressListener listener);
}

@ -0,0 +1,88 @@
package net.java.sip.communicator.service.callhistory;
import java.util.*;
/**
* Structure used for encapsulating data when writing or reading
* Call History Data. Also These records are uesd for returning data
* from the Call History Service
*
* @author Damian Minkov
*/
public class CallParticipantRecord
{
private String participantAddress = null;
private Date startTime = null;
private Date endTime = null;
/**
* Creates CallParticipantRecord
* @param participantAddress String
* @param startTime Date
* @param endTime Date
*/
public CallParticipantRecord(
String participantAddress,
Date startTime,
Date endTime)
{
this.participantAddress = participantAddress;
this.startTime = startTime;
this.endTime = endTime;
}
/**
* When participant diconnected from the call
*
* @return Date
*/
public Date getEndTime()
{
return endTime;
}
/**
* The participant address
* @return String
*/
public String getParticipantAddress()
{
return participantAddress;
}
/**
* When participant connected to the call
* @return Date
*/
public Date getStartTime()
{
return startTime;
}
/**
* Sets the time the participant joined the call
* @param startTime Date
*/
public void setStartTime(Date startTime)
{
this.startTime = startTime;
}
/**
* Sets the particiapnts address
* @param participantAddress String
*/
public void setParticipantAddress(String participantAddress)
{
this.participantAddress = participantAddress;
}
/**
* Sets the time participant leaves the call
* @param endTime Date
*/
public void setEndTime(Date endTime)
{
this.endTime = endTime;
}
}

@ -0,0 +1,163 @@
package net.java.sip.communicator.service.callhistory;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
/**
* Structure used for encapsulating data when writing or reading
* Call History Data. Also These records are uesd for returning data
* from the Call History Service
*
* @author Damian Minkov
*/
public class CallRecord
{
/**
* Possible directions of the call
*/
public final static String OUT = "out";
public final static String IN = "in";
private Call sourceCall = null;
private String direction = null;
private Vector participantRecords = new Vector();
private Date startTime = null;
private Date endTime = null;
/**
* Creates CallRecord
*/
public CallRecord()
{
}
/**
* Creates Call Record
* @param sourceCall Call
* @param direction String
* @param startTime Date
* @param endTime Date
*/
public CallRecord(
Call sourceCall,
String direction,
Date startTime,
Date endTime)
{
this.sourceCall = sourceCall;
this.direction = direction;
this.startTime = startTime;
this.endTime = endTime;
}
/**
* Finds a Participant with the supplied address
* @param address String
* @return CallParticipantRecord
*/
public CallParticipantRecord findParticipantRecord(String address)
{
Iterator iter = participantRecords.iterator();
while (iter.hasNext())
{
CallParticipantRecord item = (CallParticipantRecord) iter.next();
if (item.getParticipantAddress().equals(address))
return item;
}
return null;
}
/**
* Set the time when the call finishes
* If some participant has no end Time set we set it also
* @param endTime Date
*/
public void setEndTime(Date endTime)
{
this.endTime = endTime;
Iterator iter = participantRecords.iterator();
while (iter.hasNext())
{
CallParticipantRecord item = (CallParticipantRecord) iter.next();
if(item.getEndTime() == null)
item.setEndTime(endTime);
}
}
/**
* Sets the time when the call begins
* @param startTime Date
*/
public void setStartTime(Date startTime)
{
this.startTime = startTime;
}
/**
* The source call which this record servers
* @param sourceCall Call
*/
public void setSourceCall(Call sourceCall)
{
this.sourceCall = sourceCall;
}
/**
* Sets the direction of the call
* IN or OUT
* @param direction String
*/
public void setDirection(String direction)
{
this.direction = direction;
}
/**
* Returns the direction of the call
* IN or OUT
* @return String
*/
public String getDirection()
{
return direction;
}
/**
* Returns the time when the call has finished
* @return Date
*/
public Date getEndTime()
{
return endTime;
}
/**
* Return Vector of CallParticipantRecords
* @return Vector
*/
public Vector getParticipantRecords()
{
return participantRecords;
}
/**
* The Call source of this record
* @return Call
*/
public Call getSourceCall()
{
return sourceCall;
}
/**
* The time when the call has began
* @return Date
*/
public Date getStartTime()
{
return startTime;
}
}

@ -0,0 +1,40 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.callhistory.event;
import net.java.sip.communicator.service.history.event.*;
/**
* When searching into the call history a ProgressEvent is fired whenever
* the progress is changed. Its fired through the search process
* informing us about the current progress.
*
* @author Damian Minkov
*/
public interface CallHistorySearchProgressListener
{
/**
* The minimum value for the progress change.
* This is value indicates that the process has started.
*/
public static int PROGRESS_MINIMUM_VALUE =
HistorySearchProgressListener.PROGRESS_MINIMUM_VALUE;
/**
* The maximum value for the progress change.
* This is value indicates that the process is finished.
*/
public static int PROGRESS_MAXIMUM_VALUE =
HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE;
/**
* This method gets called when progress changes through the search process
* @param evt ProgressEvent the event holding the search condition and
* the current progress value.
*/
void progressChanged(ProgressEvent evt);
}

@ -0,0 +1,74 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.callhistory.event;
import java.util.*;
/**
* A "ProgressEvent" event gets delivered through the search process
* of CallHistoryService Service.
* The event is wrapper around the generated event from the History Service
*
* @author Damian Minkov
*/
public class ProgressEvent
extends java.util.EventObject
{
private net.java.sip.communicator.service.history.event.ProgressEvent evt;
/**
* The current progress that we will pass.
*/
private int progress = 0;
public ProgressEvent(
Object source,
net.java.sip.communicator.service.history.event.ProgressEvent evt,
int progress)
{
super(source);
this.evt = evt;
this.progress = progress;
}
/**
* Gets the current progress that will be fired.
* @return int the progress value
*/
public int getProgress()
{
return progress;
}
/**
* The end date in the search condition.
* @return Date end date value
*/
public Date getEndDate()
{
return evt.getEndDate();
}
/**
* The start date in the search condition.
* @return Date start date value
*/
public Date getStartDate()
{
return evt.getStartDate();
}
/**
* Sets the progress that will be fired
* @param progress int progress value
*/
public void setProgress(int progress)
{
this.progress = progress;
}
}

@ -0,0 +1,56 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.slick.callhistory;
import java.util.*;
import org.osgi.framework.*;
import junit.framework.*;
import net.java.sip.communicator.util.*;
/**
*
* @author Damian Minkov
*/
public class CallHistoryServiceLick extends TestSuite implements BundleActivator {
private static Logger logger = Logger.getLogger(CallHistoryServiceLick.class);
protected static BundleContext bc = null;
/**
* Start the History Sevice Implementation Compatibility Kit.
*
* @param bundleContext
* BundleContext
* @throws Exception
*/
public void start(BundleContext bundleContext)
throws Exception
{
CallHistoryServiceLick.bc = bundleContext;
setName("CallHistoryServiceLick");
Hashtable properties = new Hashtable();
properties.put("service.pid", getName());
addTest(TestCallHistoryService.suite());
bundleContext.registerService(getClass().getName(), this, properties);
logger.debug("Successfully registered " + getClass().getName());
}
/**
* stop
*
* @param bundlecontext BundleContext
* @throws Exception
*/
public void stop(BundleContext bundlecontext)
throws Exception
{
}
}

@ -0,0 +1,353 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.slick.callhistory;
import java.util.*;
import org.osgi.framework.*;
import junit.framework.*;
import net.java.sip.communicator.impl.protocol.mock.*;
import net.java.sip.communicator.service.callhistory.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
/**
* Tests call history.
* First installs the MockProtocolProvider to be able to create som calls
* The call history service stores them
* and then tests the verious find methods - does they find the calls we have
* already made
*
* @author Damian Minkov
*/
public class TestCallHistoryService
extends TestCase
{
private static final Logger logger = Logger.getLogger(TestCallHistoryService.class);
/**
* The provider that we use to make a dummy server-stored contactlist
* used for testing. The mockProvider is instantiated and registered
* by the metacontactlist slick activator.
*/
public static MockProvider mockProvider = null;
public static MockOperationSetBasicTelephony mockBTelphonyOpSet = null;
private static ServiceReference callHistoryServiceRef = null;
public static CallHistoryService callHistoryService = null;
/**
* A reference to the registration of the first mock provider.
*/
public static ServiceRegistration mockPrServiceRegistration = null;
private static Date controlDate1 = null;
private static Date controlDate2 = null;
/**
* The addresses will be used in the generated mock calls
*/
private static Vector participantAddresses = new Vector();
public TestCallHistoryService(String name)
{
super(name);
}
public static Test suite()
{
TestSuite suite = new TestSuite();
suite.addTest(
new TestCallHistoryService("setupContact"));
suite.addTest(
new TestCallHistoryService("writeRecords"));
suite.addTest(
new TestCallHistoryService("readRecords"));
suite.addTest(
new TestCallHistoryService("checkRecordCompleteness"));
return suite;
}
protected void setUp() throws Exception
{
}
protected void tearDown() throws Exception
{
}
public void setupContact()
{
// changes the history service target derictory
System.setProperty("HistoryServiceDirectory", "test-callhistory");
mockProvider = new MockProvider("CallHistoryMockUser");
//store thre presence op set of the new provider into the fixture
Map supportedOperationSets =
mockProvider.getSupportedOperationSets();
mockBTelphonyOpSet =
(MockOperationSetBasicTelephony) supportedOperationSets.get(
OperationSetBasicTelephony.class.getName());
System.setProperty(MetaContactListService.PROVIDER_MASK_PROPERTY, "1");
Hashtable mockProvProperties = new Hashtable();
mockProvProperties.put(ProtocolProviderFactory.PROTOCOL
, mockProvider.getProtocolName());
mockProvProperties.put(MetaContactListService.PROVIDER_MASK_PROPERTY,
"1");
mockPrServiceRegistration =
CallHistoryServiceLick.bc.registerService(
ProtocolProviderService.class.getName(),
mockProvider,
mockProvProperties);
logger.debug("Registered a mock protocol provider! ");
callHistoryServiceRef =
CallHistoryServiceLick.bc.
getServiceReference(CallHistoryService.class.getName());
callHistoryService =
(CallHistoryService) CallHistoryServiceLick.bc.
getService(callHistoryServiceRef);
// Will genarate 4 Calls with 4 different participants
participantAddresses.add("participant_address_1");
participantAddresses.add("participant_address_2");
participantAddresses.add("participant_address_3");
participantAddresses.add("participant_address_4");
}
/**
* First create calls
*/
public void writeRecords()
{
logger.info("write records ");
generateCall((String)participantAddresses.get(0));
controlDate1 = new Date();
generateCall((String)participantAddresses.get(1));
generateCall((String)participantAddresses.get(2));
controlDate2 = new Date();
generateCall((String)participantAddresses.get(3));
}
private void generateCall(String participant)
{
try
{
Call newCall = mockBTelphonyOpSet.placeCall(participant);
Vector v = new Vector();
Iterator iter = newCall.getCallParticipants();
while (iter.hasNext())
{
CallParticipant item = (CallParticipant) iter.next();
v.add(item);
}
waitSeconds(2000);
iter = v.iterator();
while (iter.hasNext())
{
CallParticipant item = (CallParticipant) iter.next();
mockBTelphonyOpSet.hangupCallParticipant(item);
}
}
catch (Exception ex1)
{
logger.error("Cannot place mock call", ex1);
fail("Cannot place mock call to " + participant);
}
}
private void waitSeconds(long secs)
{
Object lock = new Object();
synchronized (lock){
// wait a moment
try{
lock.wait(secs);
}
catch (InterruptedException ex){}
}
}
/**
* tests all read methods (finders)
*/
public void readRecords()
{
/**
* This must match also many calls, as tests are run many times
* but the minimum is 3
*/
Collection rs = callHistoryService.findByEndDate(controlDate2);
Iterator resultIter = rs.iterator();
assertTrue("Calls too few - findByEndDate", rs.size() >= 3);
/**
* must find 2 calls
*/
rs = callHistoryService.findByPeriod(controlDate1, controlDate2);
resultIter = rs.iterator();
assertEquals("Calls must be 2", rs.size(), 2);
CallRecord rec = (CallRecord)resultIter.next();
CallParticipantRecord participant =
(CallParticipantRecord)rec.getParticipantRecords().get(0);
assertTrue("Participant incorrect ",
participant.getParticipantAddress().
equals(participantAddresses.get(1)));
rec = (CallRecord)resultIter.next();
participant = (CallParticipantRecord)rec.getParticipantRecords().get(0);
assertTrue("Participant incorrect ",
participant.getParticipantAddress().
equals(participantAddresses.get(2)));
/**
* must find 1 record
*/
rs = callHistoryService.findByStartDate(controlDate2);
resultIter = rs.iterator();
assertEquals("Calls must be 1", rs.size(), 1);
rec = (CallRecord)resultIter.next();
participant = (CallParticipantRecord)rec.getParticipantRecords().get(0);
assertTrue("Participant incorrect ",
participant.getParticipantAddress().
equals(participantAddresses.get(3)));
/**
* Must return exactly the last 3 calls
*/
rs = callHistoryService.findLast(3);
resultIter = rs.iterator();
assertEquals("Calls must be 3", rs.size(), 3);
rec = (CallRecord)resultIter.next();
participant = (CallParticipantRecord) rec.getParticipantRecords().get(0);
assertTrue("Participant incorrect ",
participant.getParticipantAddress().
equals(participantAddresses.get(1)));
rec = (CallRecord)resultIter.next();
participant = (CallParticipantRecord) rec.getParticipantRecords().get(0);
assertTrue("Participant incorrect ",
participant.getParticipantAddress().
equals(participantAddresses.get(2)));
rec = (CallRecord)resultIter.next();
participant = (CallParticipantRecord) rec.getParticipantRecords().get(0);
assertTrue("Participant incorrect ",
participant.getParticipantAddress().
equals(participantAddresses.get(3)));
}
public void checkRecordCompleteness()
{
Vector participantAddresses = new Vector();
participantAddresses.add("some_address");
participantAddresses.add("some_new_address");
try
{
Call newCall =
mockBTelphonyOpSet.placeCall((String)participantAddresses.get(0));
Vector v = new Vector();
Iterator iter = newCall.getCallParticipants();
while (iter.hasNext())
{
CallParticipant item = (CallParticipant) iter.next();
v.add(item);
}
waitSeconds(2000);
CallParticipant newParticipant =
mockBTelphonyOpSet.addNewCallParticipant(newCall,
(String)participantAddresses.get(1));
mockBTelphonyOpSet.hangupCallParticipant(newParticipant);
waitSeconds(2000);
iter = v.iterator();
while (iter.hasNext())
{
CallParticipant item = (CallParticipant) iter.next();
mockBTelphonyOpSet.hangupCallParticipant(item);
}
}
catch (Exception ex1)
{
logger.error("Cannot place mock call", ex1);
fail("Cannot place mock call");
}
Collection lastCall = callHistoryService.findLast(1);
assertEquals("There must be 1 Call", lastCall.size(), 1);
CallRecord callRecord = (CallRecord)lastCall.iterator().next();
assertEquals("There must be 2 participants in the call",
callRecord.getParticipantRecords().size(), 2);
CallParticipantRecord callP1 =
callRecord.findParticipantRecord((String)participantAddresses.get(0));
CallParticipantRecord callP2 =
callRecord.findParticipantRecord((String)participantAddresses.get(1));
assertTrue("Second participant added after first one",
callP2.getStartTime().after(callP1.getStartTime()));
assertTrue("Second participant hanguped before first one",
callP2.getEndTime().before(callP1.getEndTime()));
}
private void dumpResult(Collection c)
{
Iterator rs = c.iterator();
while (rs.hasNext())
{
CallRecord hr = (CallRecord)rs.next();
logger.info("----------------------");
logger.info(hr.getParticipantRecords());
logger.info("----------------------");
}
}
}

@ -0,0 +1,25 @@
Bundle-Activator: net.java.sip.communicator.slick.callhistory.CallHistoryServiceLick
Bundle-Name: Call History Service Implementation Compatibility Kit
Bundle-Description: A Service Implementation Compatibility Kit for the History Service
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
Import-Package: junit.framework,
net.java.sip.communicator.slick.history,
net.java.sip.communicator.service.history.records,
net.java.sip.communicator.service.history,
net.java.sip.communicator.service.contactlist,
net.java.sip.communicator.service.callhistory,
net.java.sip.communicator.service.callhistory.event,
net.java.sip.communicator.impl.protocol.mock,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.event,
org.osgi.framework,
org.w3c.dom,
javax.xml.parsers,
net.java.sip.communicator.util,
net.java.sip.communicator.util.xml,
javax.xml.transform,
javax.xml.transform.dom,
javax.xml.transform.stream,
org.apache.xml.serializer,
Export-Package: net.java.sip.communicator.slick.callhistory,
Loading…
Cancel
Save