package com.rayo.server.verb; import java.net.URI; import javax.validation.ConstraintValidatorContext; import com.rayo.core.verb.InputMode; import com.rayo.core.verb.Ssml; import com.rayo.core.verb.Verb; import com.rayo.core.verb.VerbCommand; import com.rayo.core.verb.VerbCompleteEvent; import com.rayo.core.verb.VerbEvent; import com.rayo.server.Actor; import com.rayo.server.validation.ValidHandlerState; import com.voxeo.moho.Call; import com.voxeo.moho.Call.State; import com.voxeo.moho.IncomingCall; import com.voxeo.moho.MediaService; import com.voxeo.moho.Mixer; import com.voxeo.moho.OutgoingCall; import com.voxeo.moho.Participant; import com.voxeo.moho.Participant.JoinType; import com.voxeo.moho.media.output.AudibleResource; import com.voxeo.moho.media.output.OutputCommand; @ValidHandlerState public abstract class AbstractLocalVerbHandler<T extends Verb, S extends Participant> implements VerbHandler<T, S> { protected T model; protected S participant; protected Actor actor; private EventDispatcher eventDispatcher; private volatile boolean complete = false; @Override public void onCommand(VerbCommand command) {} @Override public Verb getModel() { return model; } @Override @SuppressWarnings("unchecked") public void setModel(Verb model) { this.model = (T)model; } @Override public S getParticipant() { return participant; } @Override public void setParticipant(S participant) { this.participant = participant; } protected AudibleResource resolveAudio(final Ssml item) { return new AudibleResource() { public URI toURI() { return item.toUri(); } }; } protected com.voxeo.moho.media.InputMode getMohoMode(InputMode mode) { switch(mode) { case ANY: return com.voxeo.moho.media.InputMode.ANY; case DTMF: return com.voxeo.moho.media.InputMode.DTMF; case VOICE: return com.voxeo.moho.media.InputMode.SPEECH; default: throw new UnsupportedOperationException("Mode not supported: " + mode); } } protected InputMode getInputMode(com.voxeo.moho.media.InputMode mode) { switch(mode) { case ANY: return InputMode.ANY; case DTMF: return InputMode.DTMF; case SPEECH: return InputMode.VOICE; default: throw new UnsupportedOperationException("Mode not supported: " + mode); } } protected OutputCommand output(Ssml items) { return new OutputCommand(resolveAudio(items)); } protected void complete(VerbCompleteEvent event) { if (!complete) { complete = true; eventDispatcher.fire(event); } } protected void fire(VerbEvent event) { eventDispatcher.fire(event); } public EventDispatcher getEventDispatcher() { return eventDispatcher; } public void setEventDispatcher(EventDispatcher eventDispatcher) { this.eventDispatcher = eventDispatcher; } @Override public boolean isComplete() { return complete; } @Override public boolean isStateValid(ConstraintValidatorContext context) { return true; } @SuppressWarnings("unchecked") protected MediaService<Participant> getMediaService() { return (MediaService<Participant>) participant; } boolean isOnHold(Participant participant) { if (participant instanceof Call) { Call call = (Call)participant; return call.isHold(); } return false; } boolean isReady(Participant participant) { if (participant instanceof Call) { Call call = (Call)participant; if (call instanceof OutgoingCall && call.getCallState() == State.INPROGRESS) { // RAYO-116 can't output to outbound call after early media is negotiated. // early media on outbound call return true; } if (call.getCallState() == State.ACCEPTED || call.getCallState() == State.CONNECTED || (call instanceof IncomingCall && ((IncomingCall)call).isAcceptedWithEarlyMedia())) { return true; } } else if (participant instanceof Mixer) { return true; } return false; } boolean canManipulateMedia() { Participant[] joinees = participant.getParticipants(); for(Participant joinee: joinees) { if (participant.getJoinType(joinee) == JoinType.DIRECT) { // On DIRECT mode media is not bridged, so no media operations are allowed return false; } } return true; } public void setActor(Actor actor) { this.actor = actor; } public Actor getActor() { return actor; } }