/**
* Copyright 2010-2011 Voxeo Corporation 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 com.voxeo.moho;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import javax.media.mscontrol.join.Joinable;
import javax.media.mscontrol.join.Joinable.Direction;
import javax.media.mscontrol.mediagroup.MediaGroup;
import org.apache.log4j.Logger;
import com.voxeo.moho.common.event.AutowiredEventListener;
import com.voxeo.moho.common.event.AutowiredEventTarget;
import com.voxeo.moho.common.event.EventDispatcher;
import com.voxeo.moho.common.event.MohoEarlyMediaEvent;
import com.voxeo.moho.common.util.InheritLogContextRunnable;
import com.voxeo.moho.common.util.Utils;
import com.voxeo.moho.event.AcceptableEvent;
import com.voxeo.moho.event.CallEvent;
import com.voxeo.moho.event.EarlyMediaEvent;
import com.voxeo.moho.event.Event;
import com.voxeo.moho.event.EventSource;
import com.voxeo.moho.event.Observer;
import com.voxeo.moho.event.RequestEvent;
import com.voxeo.moho.event.ResponseEvent;
import com.voxeo.moho.media.Input;
import com.voxeo.moho.media.Output;
import com.voxeo.moho.media.Prompt;
import com.voxeo.moho.media.Recording;
import com.voxeo.moho.media.input.InputCommand;
import com.voxeo.moho.media.output.OutputCommand;
import com.voxeo.moho.media.record.RecordCommand;
import com.voxeo.moho.remotejoin.RemoteParticipant;
import com.voxeo.moho.spi.ExecutionContext;
import com.voxeo.moho.util.IDGenerator;
import com.voxeo.moho.utils.EventListener;
public abstract class CallImpl implements Call {
private static final Logger LOG = Logger.getLogger(CallImpl.class);
protected String _id;
protected Map<String, String> _states = new ConcurrentHashMap<String, String>();
protected ExecutionContext _context;
protected EventDispatcher _dispatcher = new EventDispatcher();
protected ConcurrentHashMap<Observer, AutowiredEventListener> _observers = new ConcurrentHashMap<Observer, AutowiredEventListener>();
protected CallableEndpoint _caller;
protected CallableEndpoint _callee;
protected Map<String, Object> _attributes = new ConcurrentHashMap<String, Object>();
protected boolean _isSupervised;
protected List<Call> _peers = new ArrayList<Call>(0);
protected CallImpl(ExecutionContext context) {
_context = context;
_dispatcher.setExecutor(getThreadPool(), true);
_id = IDGenerator.generateId(_context, RemoteParticipant.RemoteParticipant_TYPE_CALL);
context.addCall(this);
}
@Override
public Output<Call> output(String text) throws MediaException {
return getMediaService().output(text);
}
@Override
public Output<Call> output(URI media) throws MediaException {
return getMediaService().output(media);
}
@Override
public Output<Call> output(OutputCommand output) throws MediaException {
return getMediaService().output(output);
}
@Override
public Prompt<Call> prompt(String text, String grammar, int repeat) throws MediaException {
return getMediaService().prompt(text, grammar, repeat);
}
@Override
public Prompt<Call> prompt(URI media, String grammar, int repeat) throws MediaException {
return getMediaService().prompt(media, grammar, repeat);
}
@Override
public Prompt<Call> prompt(OutputCommand output, InputCommand input, int repeat) throws MediaException {
return getMediaService().prompt(output, input, repeat);
}
@Override
public Input<Call> input(String grammar) throws MediaException {
return getMediaService().input(grammar);
}
@Override
public Input<Call> input(InputCommand input) throws MediaException {
return getMediaService().input(input);
}
@Override
public Recording<Call> record(URI recording) throws MediaException {
return getMediaService().record(recording);
}
@Override
public Recording<Call> record(RecordCommand command) throws MediaException {
return getMediaService().record(command);
}
@Override
public MediaGroup getMediaGroup(boolean create) {
MediaService<Call> service = this.getMediaService(create);
if (service != null) {
return service.getMediaGroup(false);
}
return null;
}
@Override
public MediaGroup getMediaGroup() {
return this.getMediaGroup(true);
}
/**
* return the media service attached to the call
*
* @param reinvite
* whether Moho Framework should automatically re-invites the call to
* {@link Participant.JoinType#BRIDGE Bridge} mode if the call is
* currently joined in {@link Participant.JoinType#DIRECT Direct}
* mode.
* @throws MediaException
* when there is media server error.
* @throws IllegalStateException
* when the call is {@link Participant.JoinType#DIRECT Direct} mode
* but reinvite is false or if the call is not answered.
*/
protected abstract MediaService<Call> getMediaService(boolean reinvite);
/**
* return the media service attached to the call. Equivalent of
* {@link #getMediaService(boolean) getMediaService(true)}.
*
* @throws MediaException
* when there is media server error.
* @throws IllegalStateException
* when the call is {@link Participant.JoinType#DIRECT Direct} mode
* but reinvite is false or if the call is not answered.
*/
protected MediaService<Call> getMediaService() {
return getMediaService(true);
}
@Override
public void hangup() {
hangup(null);
}
@SuppressWarnings("rawtypes")
@Override
public void addObserver(final Observer... observers) {
if (observers != null) {
for (final Observer observer : observers) {
if (observer instanceof EventListener) {
EventListener l = (EventListener) observer;
Class claz = Utils.getGenericType(observer);
if (claz == null) {
claz = Event.class;
}
_dispatcher.addListener(claz, l);
}
else {
final AutowiredEventListener autowire = new AutowiredEventListener(observer);
if (_observers.putIfAbsent(observer, autowire) == null) {
_dispatcher.addListener(Event.class, autowire);
}
}
}
}
}
// public void removeListener(final EventListener<?> listener) {
// _dispatcher.removeListener(listener);
// }
@Override
public void removeObserver(final Observer listener) {
final AutowiredEventListener autowiredEventListener = _observers.remove(listener);
if (autowiredEventListener != null) {
_dispatcher.removeListener(autowiredEventListener);
}
}
public <S extends EventSource, T extends Event<S>> Future<T> internalDispatch(final T event) {
return _dispatcher.fire(event, true, null);
}
@Override
public <S extends EventSource, T extends Event<S>> Future<T> dispatch(final T event, final Runnable afterExec) {
return _dispatcher.fire(event, true, afterExec);
}
@Override
public String getId() {
return _id;
}
@Override
public ApplicationContext getApplicationContext() {
return _context;
}
@Override
public String getApplicationState() {
return _states.get(AutowiredEventTarget.DEFAULT_FSM);
}
@Override
public void setApplicationState(final String state) {
_states.put(AutowiredEventTarget.DEFAULT_FSM, state);
}
public String getApplicationState(final String FSM) {
return _states.get(FSM);
}
@Override
public void setApplicationState(final String FSM, final String state) {
_states.put(FSM, state);
}
protected Executor getThreadPool() {
return _context.getExecutor();
}
@Override
@SuppressWarnings("unchecked")
public <T> T getAttribute(final String name) {
if (name == null) {
return null;
}
return (T) _attributes.get(name);
}
@Override
public Map<String, Object> getAttributeMap() {
return new HashMap<String, Object>(_attributes);
}
@Override
public void setAttribute(final String name, final Object value) {
if (value == null) {
_attributes.remove(name);
}
else {
_attributes.put(name, value);
}
}
@Override
public <S extends EventSource, T extends Event<S>> Future<T> dispatch(final T event) {
Future<T> retval = null;
if (!(event instanceof CallEvent) && !(event instanceof RequestEvent) && !(event instanceof ResponseEvent)) {
retval = this.internalDispatch(event);
}
else {
final Runnable acceptor = new InheritLogContextRunnable() {
@Override
public void run() {
if (event instanceof EarlyMediaEvent) {
if (!((MohoEarlyMediaEvent) event).isProcessed() && !((MohoEarlyMediaEvent) event).isAsync()) {
try {
((EarlyMediaEvent) event).reject(null);
}
catch (final SignalException e) {
LOG.warn(e);
}
}
}
else if (event instanceof AcceptableEvent) {
if (!((AcceptableEvent) event).isAccepted() && !((AcceptableEvent) event).isRejected()
&& !((AcceptableEvent) event).isAsync()) {
try {
((AcceptableEvent) event).accept();
}
catch (final SignalException e) {
LOG.warn(e);
}
}
}
}
};
retval = this.dispatch(event, acceptor);
}
return retval;
}
@Override
public Call[] getPeers() {
synchronized (_peers) {
return _peers.toArray(new CallImpl[_peers.size()]);
}
}
@Override
public Joint join() {
return join(Joinable.Direction.DUPLEX);
}
@Override
public Joint join(final CallableEndpoint other, final JoinType type, final Direction direction) {
return join(other, type, direction, null);
}
}