package com.voxeo.moho.sip; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.media.mscontrol.MsControlException; import javax.media.mscontrol.join.Joinable.Direction; import javax.servlet.sip.SipServletRequest; import javax.servlet.sip.SipServletResponse; import org.apache.log4j.Logger; import com.voxeo.moho.Participant.JoinType; import com.voxeo.moho.event.JoinCompleteEvent; import com.voxeo.moho.sip.SIPCall.State; public class DirectNO2MultipleNOJoinDelegate extends JoinDelegate { private static final Logger LOG = Logger.getLogger(DirectNO2MultipleNOJoinDelegate.class); protected List<SIPCallImpl> candidateCalls = new LinkedList<SIPCallImpl>(); protected boolean _suppressEarlyMedia; protected SipServletResponse _responseCall1; protected SipServletResponse _responseCall2; protected Object syncLock = new Object(); protected DirectNO2MultipleNOJoinDelegate(JoinType type, Direction direction, SIPCallImpl call1, boolean suppressEarlyMedia, List<SIPCallImpl> others) { _call1 = call1; _suppressEarlyMedia = suppressEarlyMedia; _direction = direction; _joinType = type; candidateCalls.addAll(others); } @Override public void doJoin() throws Exception { super.doJoin(); // TODO should we use mock SDP (SDP connection address 0.0.0.0 or with // sendonly // atrribute) to disable 183 here? SIPHelper.remove100relSupport(_call1.getSipInitnalRequest()); ((SIPOutgoingCall) _call1).call(null); } @Override protected void doInviteResponse(SipServletResponse res, SIPCallImpl call, Map<String, String> headers) throws Exception { try { synchronized (syncLock) { if (SIPHelper.isProvisionalResponse(res)) { SIPHelper.trySendPrack(res); return; } else if (SIPHelper.isSuccessResponse(res)) { if (_call1.equals(call)) { _responseCall1 = res; _call1.setSIPCallState(State.ANSWERED); Exception exception = null; int exceptionCount = 0; for (SIPCallImpl candidate : candidateCalls) { try { SIPHelper.remove100relSupport(candidate.getSipInitnalRequest()); ((SIPOutgoingCall) call).call(_call1.getRemoteSdp(), _call1.getSipSession().getApplicationSession()); } catch (Exception ex) { exceptionCount++; exception = ex; LOG.error("Exception when trying call " + call, ex); } if (exceptionCount == candidateCalls.size()) { LOG.error("Exception when joining using delegate " + this, exception); done(JoinCompleteEvent.Cause.ERROR, exception); failCall(_call1, exception); throw exception; } } } else { res.createAck().send(); if (_call2 != null) { // receive second success response, ignore it. return; } _responseCall2 = res; _call2 = call; candidateCalls.remove(call); disconnectCalls(candidateCalls); _call2.setSIPCallState(State.ANSWERED); SipServletRequest ack1 = _responseCall1.createAck(); SIPHelper.copyContent(_responseCall2, ack1); ack1.send(); successJoin(); } } else if (SIPHelper.isErrorResponse(res)) { if (_call1.equals(call)) { LOG.warn("INVITE call1 got error response, failed join on delegate " + this); done(this.getJoinCompleteCauseByResponse(res), this.getExceptionByResponse(res)); this.disconnectCall(_call1, true, getCallCompleteCauseByResponse(res), getExceptionByResponse(res)); } else { candidateCalls.remove(call); if (candidateCalls.isEmpty() && _call2 == null) { _call2 = call; done(this.getJoinCompleteCauseByResponse(res), this.getExceptionByResponse(res)); } disconnectCall(call, true, getCallCompleteCauseByResponse(res), getExceptionByResponse(res)); try { if (_responseCall1 != null) { _responseCall1.createAck().send(); } } catch (Exception ex) { LOG.debug("Exception when sending back ACK", ex); } disconnectCall(_call1, true, getCallCompleteCauseByResponse(res), getExceptionByResponse(res)); } } } } catch (Exception ex) { LOG.error("Exception when joining using delegate " + this, ex); done(JoinCompleteEvent.Cause.ERROR, ex); failCall(_call1, ex); if (_call2 != null) { failCall(_call2, ex); } if (!candidateCalls.isEmpty()) { disconnectCalls(candidateCalls); } throw ex; } } private void successJoin() throws MsControlException { _peer = _call2; doDisengage(_call1, JoinType.DIRECT); _call1.linkCall(_call2, JoinType.DIRECT, _direction); done(JoinCompleteEvent.Cause.JOINED, null); } }