/*
* 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.service.protocol;
import java.util.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* Keeps a list of all calls currently active and maintained by this protocol
* provider. Offers methods for finding a call by its ID, peer session
* and others. This class is meant for use by protocol implementations and
* cannot be accessed from other bundles.
*
* @param <T> <tt>Call</tt>
* @param <U> <tt>OperationSetBasicTelephony</tt>
* @author Emil Ivov
*/
public abstract class ActiveCallsRepository<T extends Call,
U extends OperationSetBasicTelephony<? extends ProtocolProviderService>>
extends CallChangeAdapter
{
/**
* The <tt>Logger</tt> used by the <tt>ActiveCallsRepository</tt>
* class and its instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(ActiveCallsRepository.class);
/**
* A table mapping call ids against call instances.
*/
private final Hashtable<String, T> activeCalls = new Hashtable<String, T>();
/**
* The operation set that created us. Instance is mainly used for firing
* events when necessary.
*/
protected final U parentOperationSet;
/**
* Creates a new instance of this repository.
*
* @param opSet a reference to the
* <tt>AbstractOperationSetBasicTelephony</tt> extension that created us.
*/
public ActiveCallsRepository(U opSet)
{
this.parentOperationSet = opSet;
}
/**
* Adds the specified call to the list of calls tracked by this repository.
* @param call CallSipImpl
*/
public void addCall(T call)
{
activeCalls.put(call.getCallID(), call);
call.addCallChangeListener(this);
}
/**
* If <tt>evt</tt> indicates that the call has been ended we remove it from
* the repository.
* @param evt the <tt>CallChangeEvent</tt> instance containing the source
* calls and its old and new state.
*/
@Override
public void callStateChanged(CallChangeEvent evt)
{
if(evt.getEventType().equals(CallChangeEvent.CALL_STATE_CHANGE)
&& evt.getNewValue().equals(CallState.CALL_ENDED))
{
T sourceCall = activeCalls.remove(evt.getSourceCall().getCallID());
if (logger.isTraceEnabled())
{
logger.trace(
"Removing call "
+ sourceCall
+ " from the list of active calls"
+ " because it entered an ENDED state");
}
fireCallEvent(CallEvent.CALL_ENDED, sourceCall);
}
}
/**
* Returns an iterator over all currently active (non-ended) calls.
*
* @return an iterator over all currently active (non-ended) calls.
*/
public Iterator<T> getActiveCalls()
{
synchronized(activeCalls)
{
/*
* Given that we know the elements that will go into the new List,
* it is more optimal in terms of memory and execution time to use
* ArrayList rather than LinkedList.
*/
return new ArrayList<T>(activeCalls.values()).iterator();
}
}
/**
* Returns the number of calls currently tracked by this repository.
*
* @return the number of calls currently tracked by this repository.
*/
public int getActiveCallCount()
{
synchronized (activeCalls)
{
return activeCalls.size();
}
}
/**
* 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)
{
fireCallEvent(eventID, sourceCall, null);
}
/**
* Creates and dispatches a <tt>CallEvent</tt> notifying registered
* listeners that an event with id <tt>eventID</tt> has occurred on
* <tt>sourceCall</tt>.
* <p>
* TODO The method is ugly because it can be implemented if
* <tt>parentOperationSet</tt> is an
* <tt>AbstractOperationSetBasicTelephony</tt>. But after the move of the
* latter in the <tt>.service.protocol.media</tt> package, it is not visible
* here.
* </p>
*
* @param eventID the ID of the event to dispatch
* @param sourceCall the call on which the event has occurred
* @param cause the <tt>CallChangeEvent</tt>, if any, which is the cause
* that necessitated a new <tt>CallEvent</tt> to be fired
*/
protected abstract void fireCallEvent(
int eventID,
Call sourceCall,
CallChangeEvent cause);
}