| /** |
| * 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"); |
| } |
| } |
| |
| } |