package com.voxeo.moho.sip; import java.util.LinkedList; import java.util.List; import javax.media.mscontrol.join.Joinable.Direction; import javax.media.mscontrol.networkconnection.NetworkConnection; import javax.servlet.sip.SipServletRequest; import javax.servlet.sip.SipServletResponse; import org.apache.log4j.Logger; import com.voxeo.moho.ApplicationContextImpl; import com.voxeo.moho.JoinData; import com.voxeo.moho.Participant.JoinType; import com.voxeo.moho.State; import com.voxeo.moho.event.JoinCompleteEvent; import com.voxeo.moho.event.JoinCompleteEvent.Cause; import com.voxeo.moho.event.Observer; import com.voxeo.moho.media.dialect.MediaDialect; import com.voxeo.moho.util.SDPUtils; // NOTE that moho application should process redirect event. public class MultipleNOBridgeJoinDelegate extends JoinDelegate implements Observer { private static final Logger LOG = Logger.getLogger(MultipleNOBridgeJoinDelegate.class); protected List<SIPCallImpl> candidateCalls = new LinkedList<SIPCallImpl>(); protected boolean joinedOutgoingCall; protected boolean waitingACK; protected MultipleNOBridgeJoinDelegate(JoinType type, Direction direction, SIPCallImpl call1, List<SIPCallImpl> others) { _call1 = call1; _direction = direction; _joinType = type; candidateCalls.addAll(others); } @Override public void doJoin() throws Exception { super.doJoin(); if (_call1.getMediaObject() == null && _call1 instanceof SIPOutgoingCall) { _call1.join(Direction.DUPLEX); return; } if (!joinedOutgoingCall) { for (SIPCallImpl other : candidateCalls) { other.addObserver(this); other.setJoiningPeer(new JoinData(_call1, _direction, _joinType)); other.join(Direction.DUPLEX); } joinedOutgoingCall = true; } } @State public synchronized void onJoinCompleteEvent(JoinCompleteEvent event) { event.getSource().removeObserver(this); SIPCallImpl call = (SIPCallImpl) event.getSource(); call.setJoiningPeer(null); if (done || _call2 != null) { return; } if (event.getCause() == JoinCompleteEvent.Cause.JOINED) { try { _call2 = call; candidateCalls.remove(event.getSource()); disconnectCalls(candidateCalls); if (_call1 instanceof SIPIncomingCall && _call1.getSIPCallState() == SIPCall.State.PROGRESSED) { final SipServletResponse res = _call1.getSipInitnalRequest().createResponse(SipServletResponse.SC_OK); if (_call1.getLocalSDP() != null) { res.setContent(SDPUtils.formulateSDP(_call1, _call1.getLocalSDP()), "application/sdp"); } waitingACK = true; res.send(); } else { if (_call1 instanceof SIPIncomingCall && _call1.getMediaObject() == null) { _call1.join().get(); } successJoin(call); } } catch (final Exception e) { LOG.error("Exception when doing join on delegate " + this, e); done(Cause.ERROR, e); failCall(call, e); if (!candidateCalls.isEmpty()) { disconnectCalls(candidateCalls); } } } else { candidateCalls.remove(event.getSource()); if (candidateCalls.isEmpty() && _call2 == null) { done(event.getCause(), getExceptionByResponse(call.getLastReponse())); } disconnectCall(call, true, null, null); } } @Override protected void doAck(final SipServletRequest req, final SIPCallImpl call) throws Exception { if (waitingACK) { call.setSIPCallState(SIPCall.State.ANSWERED); // call.processSDPAnswer(req); successJoin(_call2); } } private void successJoin(SIPCallImpl call) { try { _peer = _call2; MediaDialect dialect = ((ApplicationContextImpl) _call1.getApplicationContext()).getDialect(); dialect.setDtmfPassThrough((NetworkConnection) _call1.getMediaObject(), dtmfPassThrough); dialect.setDtmfPassThrough((NetworkConnection) call.getMediaObject(), dtmfPassThrough); _call1.linkCall(call, _joinType, _direction); _call1.setJoiningPeer(null); call.setJoiningPeer(null); done(Cause.JOINED, null); } catch (final Exception e) { LOG.error("Exception when doing join on delegate " + this, e); done(Cause.ERROR, e); failCall(call, e); if (!candidateCalls.isEmpty()) { disconnectCalls(candidateCalls); } } } }