blob: d3f4f888d2ecf68140c2c31e0a0e394525c340bb [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.camel.component.sip.listener;
import java.text.ParseException;
import java.util.UUID;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipListener;
import javax.sip.SipProvider;
import javax.sip.TransactionTerminatedEvent;
import javax.sip.address.SipURI;
import javax.sip.header.EventHeader;
import javax.sip.header.SubscriptionStateHeader;
import javax.sip.header.ToHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import org.apache.camel.component.sip.SipPresenceAgent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SipPresenceAgentListener implements SipListener, SipMessageCodes {
private static final transient Logger LOG = LoggerFactory.getLogger(SipPresenceAgentListener.class);
protected Dialog dialog;
protected int notifyCount;
private SipPresenceAgent sipPresenceAgent;
public SipPresenceAgentListener(SipPresenceAgent sipPresenceAgent) {
this.sipPresenceAgent = sipPresenceAgent;
}
public void processRequest(RequestEvent requestEvent) {
Request request = requestEvent.getRequest();
ServerTransaction serverTransactionId = requestEvent.getServerTransaction();
if (LOG.isDebugEnabled()) {
LOG.debug("Request: " + request.getMethod());
LOG.debug("Server Transaction Id:" + serverTransactionId);
}
if (request.getMethod().equals(Request.SUBSCRIBE)) {
processSubscribe(requestEvent, serverTransactionId);
} else if (request.getMethod().equals(Request.PUBLISH)) {
processPublish(requestEvent, serverTransactionId);
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Received expected request with method: " + request.getMethod() + ". No further processing done");
}
}
}
private void sendNotification(EventHeader eventHeader, boolean isInitial, Object body) throws SipException, ParseException {
/*
* NOTIFY requests MUST contain a "Subscription-State" header with a
* value of "active", "pending", or "terminated". The "active" value
* indicates that the subscription has been accepted and has been
* authorized (in most cases; see section 5.2.). The "pending" value
* indicates that the subscription has been received, but that
* policy information is insufficient to accept or deny the
* subscription at this time. The "terminated" value indicates that
* the subscription is not active.
*/
Request notifyRequest = dialog.createRequest("NOTIFY");
// Mark the contact header, to check that the remote contact is updated
((SipURI)sipPresenceAgent.getConfiguration().getContactHeader().getAddress().getURI()).setParameter(
sipPresenceAgent.getConfiguration().getFromUser(), sipPresenceAgent.getConfiguration().getFromHost());
SubscriptionStateHeader sstate;
if (isInitial) {
// Initial state is pending, second time we assume terminated (Expires==0)
sstate =
sipPresenceAgent.getConfiguration().getHeaderFactory().createSubscriptionStateHeader(isInitial ? SubscriptionStateHeader.PENDING : SubscriptionStateHeader.TERMINATED);
// Need a reason for terminated
if (sstate.getState().equalsIgnoreCase("terminated")) {
sstate.setReasonCode("deactivated");
}
} else {
sstate = sipPresenceAgent.getConfiguration().getHeaderFactory().createSubscriptionStateHeader(SubscriptionStateHeader.ACTIVE);
}
notifyRequest.addHeader(sstate);
notifyRequest.setHeader(eventHeader);
notifyRequest.setHeader(sipPresenceAgent.getConfiguration().getContactHeader());
notifyRequest.setContent(body, sipPresenceAgent.getConfiguration().getContentTypeHeader());
if (LOG.isDebugEnabled()) {
LOG.debug("Sending the following NOTIFY request to Subscriber: " + notifyRequest);
}
ClientTransaction clientTransactionId = sipPresenceAgent.getProvider().getNewClientTransaction(notifyRequest);
dialog.sendRequest(clientTransactionId);
}
private void processPublish(RequestEvent requestEvent,
ServerTransaction serverTransactionId) {
try {
Request request = requestEvent.getRequest();
if (LOG.isDebugEnabled()) {
LOG.debug("SipPresenceAgentListener: Received a Publish request, sending OK");
LOG.debug("SipPresenceAgentListener request: " + request);
}
EventHeader eventHeader = (EventHeader) requestEvent.getRequest().getHeader(EventHeader.NAME);
Response response = sipPresenceAgent.getConfiguration().getMessageFactory().createResponse(202, request);
sipPresenceAgent.getProvider().sendResponse(response);
// Send notification to subscriber
sendNotification(eventHeader, false, request.getContent());
} catch (Exception e) {
LOG.error("Exception thrown during publish/notify processing in the Sip Presence Agent Listener", e);
}
}
public void processSubscribe(RequestEvent requestEvent,
ServerTransaction serverTransaction) {
SipProvider sipProvider = (SipProvider) requestEvent.getSource();
Request request = requestEvent.getRequest();
try {
if (LOG.isDebugEnabled()) {
LOG.debug("SipPresenceAgentListener: Received a Subscribe request, sending OK");
LOG.debug("SipPresenceAgentListener request: " + request);
}
EventHeader eventHeader = (EventHeader) request.getHeader(EventHeader.NAME);
if (eventHeader == null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Cannot find event header.... dropping request.");
}
return;
}
// Always create a ServerTransaction, best as early as possible in the code
Response response = null;
ServerTransaction st = requestEvent.getServerTransaction();
if (st == null) {
st = sipProvider.getNewServerTransaction(request);
}
// Check if it is an initial SUBSCRIBE or a refresh / unsubscribe
boolean isInitial = requestEvent.getDialog() == null;
if (isInitial) {
String toTag = UUID.randomUUID().toString();
response = sipPresenceAgent.getConfiguration().getMessageFactory().createResponse(202, request);
ToHeader toHeader = (ToHeader) response.getHeader(ToHeader.NAME);
toHeader.setTag(toTag); // Application is supposed to set.
this.dialog = st.getDialog();
// subscribe dialogs do not terminate on bye.
this.dialog.terminateOnBye(false);
} else {
response = sipPresenceAgent.getConfiguration().getMessageFactory().createResponse(200, request);
}
// Both 2xx response to SUBSCRIBE and NOTIFY need a Contact
response.addHeader(sipPresenceAgent.getConfiguration().getContactHeader());
// Expires header is mandatory in 2xx responses to SUBSCRIBE
response.addHeader(sipPresenceAgent.getConfiguration().getExpiresHeader());
st.sendResponse(response);
if (LOG.isDebugEnabled()) {
LOG.debug("SipPresenceAgentListener: Sent OK Message");
LOG.debug("SipPresenceAgentListener response: " + response);
}
sendNotification(eventHeader, isInitial, request.getContent());
} catch (Throwable e) {
LOG.error("Exception thrown during Notify processing in the SipPresenceAgentListener.", e);
}
}
public synchronized void processResponse(ResponseEvent responseReceivedEvent) {
Response response = responseReceivedEvent.getResponse();
Integer statusCode = response.getStatusCode();
if (SIP_MESSAGE_CODES.containsKey(statusCode)) {
LOG.debug(SIP_MESSAGE_CODES.get(statusCode) + " received from Subscriber");
}
}
public void processTimeout(javax.sip.TimeoutEvent timeoutEvent) {
if (LOG.isWarnEnabled()) {
LOG.warn("TimeoutEvent received at Sip Subscription Listener");
}
}
public void processIOException(IOExceptionEvent exceptionEvent) {
if (LOG.isWarnEnabled()) {
LOG.warn("IOExceptionEvent received at SipPresenceAgentListener");
}
}
public void processTransactionTerminated(
TransactionTerminatedEvent transactionTerminatedEvent) {
if (LOG.isWarnEnabled()) {
LOG.warn("TransactionTerminatedEvent received at SipPresenceAgentListener");
}
}
public void processDialogTerminated(
DialogTerminatedEvent dialogTerminatedEvent) {
if (LOG.isWarnEnabled()) {
LOG.warn("DialogTerminatedEvent received at SipPresenceAgentListener");
}
}
}