/**
* Copyright 2010 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.sip;
import java.util.Map;
import javax.media.mscontrol.MsControlException;
import javax.media.mscontrol.join.Joinable.Direction;
import javax.servlet.sip.Rel100Exception;
import javax.servlet.sip.SipServletMessage;
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;
import com.voxeo.moho.util.SDPUtils;
public class DirectAI2NOJoinDelegate extends JoinDelegate {
private static final Logger LOG = Logger.getLogger(DirectAI2NOJoinDelegate.class);
protected Direction _direction;
protected SipServletResponse _response;
protected SipServletResponse _reliable183Resp;
protected boolean _reInvited;
protected SipServletResponse _responseCall1;
protected SipServletRequest _pendingUpdateCall2;
protected Object syncLock = new Object();
protected DirectAI2NOJoinDelegate(final SIPIncomingCall call1, final SIPOutgoingCall call2,
final Direction direction, final SIPCallImpl peer) {
_call1 = call1;
_call2 = call2;
_direction = direction;
_peer = peer;
}
@Override
public void doJoin() throws Exception {
super.doJoin();
((SIPOutgoingCall) _call2).setContinueRouting(_call1);
((SIPOutgoingCall) _call2).call(null, _call1.getSipSession().getApplicationSession());
}
@Override
protected void doUpdate(SipServletRequest req, SIPCallImpl call, Map<String, String> headers) throws Exception {
if (SIPHelper.getRawContentWOException(req) == null) {
req.createResponse(200).send();
}
else if (_call2.equals(call)) {
if (_response != null) {
LOG.warn("Receive UPDATE after INVITE processed, return peer remote SDP.");
SipServletResponse resp = req.createResponse(200);
resp.setContent(SDPUtils.formulateSDP(_call2, _call1.getRemoteSdp()), "application/sdp");
resp.send();
}
else {
_pendingUpdateCall2 = req;
reInviteCall1(req);
}
}
}
@Override
protected void doInviteResponse(final SipServletResponse res, final SIPCallImpl call,
final Map<String, String> headers) throws Exception {
if (SIPHelper.isErrorResponse(res)) {
if(_response != null && _call1.equals(call)) {
try{
_response.createAck().send();
_call2.setSIPCallState(State.ANSWERED);
}
catch(Exception ex) {
LOG.error("Exception when sending ACK.", ex);
}
}
done(getJoinCompleteCauseByResponse(res), getExceptionByResponse(res));
disconnectCall(_call2, true, this.getCallCompleteCauseByResponse(res), this.getExceptionByResponse(res));
}
else if (SIPHelper.isProvisionalResponse(res) && _call2.equals(call)) {
_call2.setSIPCallState(SIPCall.State.ANSWERING);
try {
if (SIPHelper.getRawContentWOException(res) != null && SIPHelper.needPrack(res)) {
if(_reliable183Resp != null) {
// this is a re-send of 183 response, ignore it
return;
}
_reliable183Resp = res;
reInviteCall1(res);
_reInvited = true;
}
else {
SIPHelper.trySendPrack(res);
}
}
catch (Exception e) {
done(JoinCompleteEvent.Cause.ERROR, e);
failCall(_call2, e);
throw e;
}
}
else if (SIPHelper.isProvisionalResponse(res) && _call1.equals(call)) {
SIPHelper.trySendPrack(res);
}
else if (SIPHelper.isSuccessResponse(res)) {
try {
boolean successJoin = false;
synchronized (syncLock) {
if (_call2.equals(call)) {
_response = res;
if (!_reInvited) {
reInviteCall1(res);
}
else if (_responseCall1 != null) {
SipServletRequest ack2 = res.createAck();
if (SIPHelper.getRawContentWOException(res) != null) {
SIPHelper.copyContent(_responseCall1, ack2);
}
_call2.setSIPCallState(State.ANSWERED);
ack2.send();
successJoin = true;
}
}
else if (_call1.equals(call)) {
res.createAck().send();
if (_response != null) {
SipServletRequest ack2 = _response.createAck();
if (SIPHelper.getRawContentWOException(_response) != null) {
SIPHelper.copyContent(res, ack2);
}
_call2.setSIPCallState(State.ANSWERED);
ack2.send();
successJoin = true;
}
else {
_responseCall1 = res;
if (_pendingUpdateCall2 != null) {
SipServletResponse updateResp = _pendingUpdateCall2.createResponse(200);
SIPHelper.copyContent(res, updateResp);
updateResp.send();
_pendingUpdateCall2 = null;
}
else if (_reliable183Resp != null) {
try {
doDisengage(_call1, JoinType.DIRECT);
SipServletRequest prack = _reliable183Resp.createPrack();
_reliable183Resp = null;
SIPHelper.copyContent(res, prack);
prack.send();
}
catch (Rel100Exception ex) {
LOG.warn(ex.getMessage());
}
catch (IllegalStateException ex) {
LOG.warn(ex.getMessage());
}
}
}
}
}
if (successJoin) {
successJoin();
}
}
catch (final Exception e) {
done(JoinCompleteEvent.Cause.ERROR, e);
failCall(_call2, e);
throw e;
}
}
}
private void successJoin() throws MsControlException {
doDisengage(_call1, JoinType.DIRECT);
_call1.linkCall(_call2, JoinType.DIRECT, _direction);
done(JoinCompleteEvent.Cause.JOINED, null);
}
private void reInviteCall1(SipServletMessage message) throws Exception {
_call1.reInviteRemote(message.getContent(), null, null);
}
}