package org.mobicents.slee.sipevent.server.subscription; import java.text.ParseException; import java.util.List; import javax.naming.Context; import javax.naming.InitialContext; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.sip.Dialog; import javax.sip.RequestEvent; import javax.sip.ResponseEvent; import javax.sip.ServerTransaction; import javax.sip.address.AddressFactory; import javax.sip.header.ContentTypeHeader; import javax.sip.header.HeaderFactory; import javax.sip.message.MessageFactory; import javax.sip.message.Response; import javax.slee.ActivityContextInterface; import javax.slee.ActivityEndEvent; import javax.slee.Address; import javax.slee.ChildRelation; import javax.slee.CreateException; import javax.slee.RolledBackContext; import javax.slee.Sbb; import javax.slee.SbbContext; import javax.slee.facilities.ActivityContextNamingFacility; import javax.slee.facilities.TimerEvent; import javax.slee.facilities.TimerFacility; import javax.slee.facilities.TimerID; import javax.slee.facilities.TimerOptions; import javax.slee.facilities.TimerPreserveMissed; import javax.slee.nullactivity.NullActivity; import javax.slee.nullactivity.NullActivityContextInterfaceFactory; import javax.slee.nullactivity.NullActivityFactory; import javax.slee.serviceactivity.ServiceActivity; import javax.slee.serviceactivity.ServiceActivityFactory; import javax.slee.serviceactivity.ServiceStartedEvent; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import net.java.slee.resource.sip.SipActivityContextInterfaceFactory; import net.java.slee.resource.sip.SleeSipProvider; import org.apache.log4j.Logger; import org.mobicents.slee.sipevent.server.internal.InternalNotifyEvent; import org.mobicents.slee.sipevent.server.internal.InternalSubscriptionHandler; import org.mobicents.slee.sipevent.server.subscription.eventlist.MultiPart; import org.mobicents.slee.sipevent.server.subscription.jmx.SubscriptionControlManagement; import org.mobicents.slee.sipevent.server.subscription.jmx.SubscriptionControlManagementMBean; import org.mobicents.slee.sipevent.server.subscription.pojo.Subscription; import org.mobicents.slee.sipevent.server.subscription.pojo.SubscriptionKey; import org.mobicents.slee.sipevent.server.subscription.pojo.Subscription.Event; import org.mobicents.slee.sipevent.server.subscription.sip.SipSubscriptionHandler; import org.mobicents.slee.sipevent.server.subscription.winfo.WInfoSubscriptionHandler; /** * Sbb to control subscriptions of sip events in a dialog * * @author Eduardo Martins * */ public abstract class SubscriptionControlSbb implements Sbb, SubscriptionControlSbbLocalObject { private static final Logger logger = Logger .getLogger(SubscriptionControlSbb.class); /** * the Management MBean */ private static final SubscriptionControlManagement configuration = new SubscriptionControlManagement(); /** * JAIN-SIP provider & factories * * @return */ private SipActivityContextInterfaceFactory sipActivityContextInterfaceFactory; private SleeSipProvider sipProvider; private AddressFactory addressFactory; private MessageFactory messageFactory; private HeaderFactory headerFactory; /** * SLEE Facilities */ private TimerFacility timerFacility; private ActivityContextNamingFacility activityContextNamingfacility; private NullActivityContextInterfaceFactory nullACIFactory; private NullActivityFactory nullActivityFactory; /** * SbbObject's sbb context */ private SbbContext sbbContext; // GETTERS public SipSubscriptionHandler getSipSubscribeHandler() { return new SipSubscriptionHandler(this); } public WInfoSubscriptionHandler getWInfoSubscriptionHandler() { return new WInfoSubscriptionHandler(this); } public InternalSubscriptionHandler getInternalSubscriptionHandler() { return new InternalSubscriptionHandler(this); } public ActivityContextNamingFacility getActivityContextNamingfacility() { return activityContextNamingfacility; } public AddressFactory getAddressFactory() { return addressFactory; } public HeaderFactory getHeaderFactory() { return headerFactory; } public MessageFactory getMessageFactory() { return messageFactory; } public NullActivityContextInterfaceFactory getNullACIFactory() { return nullACIFactory; } public NullActivityFactory getNullActivityFactory() { return nullActivityFactory; } public SbbContext getSbbContext() { return sbbContext; } public SipActivityContextInterfaceFactory getSipActivityContextInterfaceFactory() { return sipActivityContextInterfaceFactory; } public SleeSipProvider getSipProvider() { return sipProvider; } public TimerFacility getTimerFacility() { return timerFacility; } /** * Retrieves the current configuration for this component from an MBean * * @return */ public SubscriptionControlManagementMBean getConfiguration() { return configuration; } // --- JPA STUFF private static EntityManagerFactory initEntityManagerFactory() { try { TransactionManager txMgr = (TransactionManager) new InitialContext() .lookup("java:/TransactionManager"); Transaction tx = null; try { if (txMgr.getTransaction() != null) { tx = txMgr.suspend(); } } catch (Exception e) { // TODO: handle exception } try { return Persistence .createEntityManagerFactory("sipevent-subscription-pu"); } finally { if (tx != null) { txMgr.resume(tx); } } } catch (Exception e) { logger.error(e.getMessage(), e); return null; } } private static void stopEntityManagerFactory() { try { TransactionManager txMgr = (TransactionManager) new InitialContext() .lookup("java:/TransactionManager"); Transaction tx = null; try { if (txMgr.getTransaction() != null) { tx = txMgr.suspend(); } } catch (Exception e) { // TODO: handle exception } try { entityManagerFactory.close(); } finally { if (tx != null) { txMgr.resume(tx); } } } catch (Exception e) { logger.error(e.getMessage(), e); } } private static EntityManagerFactory entityManagerFactory = initEntityManagerFactory(); public EntityManager getEntityManager() { return entityManagerFactory.createEntityManager(); } // -- STORAGE OF PARENT SBB (USED BY INTERNAL CLIENTS) public void setParentSbb( SubscriptionClientControlParentSbbLocalObject parentSbb) { setParentSbbCMP(parentSbb); } public abstract SubscriptionClientControlParentSbbLocalObject getParentSbbCMP(); public abstract void setParentSbbCMP( SubscriptionClientControlParentSbbLocalObject value); // --- CONCRETE IMPLEMENTATION CHILD SBB public abstract ChildRelation getImplementedControlChildRelation(); public abstract ImplementedSubscriptionControlSbbLocalObject getImplementedControlChildSbbCMP(); public abstract void setImplementedControlChildSbbCMP( ImplementedSubscriptionControlSbbLocalObject value); public ImplementedSubscriptionControlSbbLocalObject getImplementedControlChildSbb() { ImplementedSubscriptionControlSbbLocalObject childSbb = getImplementedControlChildSbbCMP(); if (childSbb == null) { try { childSbb = (ImplementedSubscriptionControlSbbLocalObject) getImplementedControlChildRelation() .create(); } catch (Exception e) { logger.error("Failed to create child sbb",e); return null; } setImplementedControlChildSbbCMP(childSbb); childSbb .setParentSbb((ImplementedSubscriptionControlParentSbbLocalObject) this.sbbContext .getSbbLocalObject()); } return childSbb; } // --- EVENT LIST PROCESSING CHILD SBB public abstract ChildRelation getEventListControlChildRelation(); public abstract EventListSubscriptionControlSbbLocalObject getEventListControlChildSbbCMP(); public abstract void setEventListControlChildSbbCMP( EventListSubscriptionControlSbbLocalObject value); public EventListSubscriptionControlSbbLocalObject getEventListControlChildSbb() { EventListSubscriptionControlSbbLocalObject childSbb = getEventListControlChildSbbCMP(); if (childSbb == null) { try { childSbb = (EventListSubscriptionControlSbbLocalObject) getEventListControlChildRelation() .create(); } catch (Exception e) { logger.error("Failed to create child sbb",e); return null; } setEventListControlChildSbbCMP(childSbb); childSbb .setParentSbb((EventListSubscriptionControlParentSbbLocalObject) this.sbbContext .getSbbLocalObject()); } return childSbb; } // ----------- EVENT HANDLERS /** * if event is for this service starts mbean */ public void onServiceStartedEvent(ServiceStartedEvent event, ActivityContextInterface aci) { // we want to stay attached to this service activity, to receive the // activity end event on service deactivation try { // get this service activity ServiceActivity sa = ((ServiceActivityFactory) new InitialContext() .lookup("java:comp/env/slee/serviceactivity/factory")) .getActivity(); if (!sa.equals(aci.getActivity())) { aci.detach(this.sbbContext.getSbbLocalObject()); } else { // starts the mbean configuration.startService(); // init RLS cache getEventListControlChildSbb().initRLSCache(); } } catch (Exception e) { logger.error("failed to process service started event", e); } } /** * If it's a service activity must be the one for this service. It then * closes the jpa EM factory and tops the MBean * * @param event * @param aci */ public void onActivityEndEvent(ActivityEndEvent event, ActivityContextInterface aci) { // close entity manager factory on service deactivation Object activity = aci.getActivity(); if (activity instanceof ServiceActivity) { getEventListControlChildSbb().shutdownRLSCache(); configuration.stopService(); stopEntityManagerFactory(); } } /** * event handler for initial subscribe, which is out of dialog * * @param event * @param aci */ public void onSubscribeOutOfDialog(RequestEvent event, ActivityContextInterface aci) { new SipSubscriptionHandler(this).processRequest(event, aci); } /** * event handler for in dialog subscribe * * @param event * @param aci */ public void onSubscribeInDialog(RequestEvent event, ActivityContextInterface aci) { // store server transaction id, we need it get it from the dialog // activity new SipSubscriptionHandler(this).processRequest(event, aci); } /** * An error as the final response of a NOTIFY sent by this server. * * @param event * @param aci */ public void onResponseClientErrorEvent(ResponseEvent event, ActivityContextInterface aci) { // we got a error response from a notify, new SipSubscriptionHandler(this).getRemoveSipSubscriptionHandler() .removeSipSubscriptionOnNotifyError(event); } /** * An error as the final response of a NOTIFY sent by this server. * * @param event * @param aci */ public void onResponseServerErrorEvent(ResponseEvent event, ActivityContextInterface aci) { // we got a error response from a notify, new SipSubscriptionHandler(this).getRemoveSipSubscriptionHandler() .removeSipSubscriptionOnNotifyError(event); } /** * a timer has occurred on a subscription * * @param event * @param aci */ public void onTimerEvent(TimerEvent event, ActivityContextInterface aci) { Object activity = aci.getActivity(); Dialog dialog = null; if (activity instanceof Dialog) { dialog = (Dialog) activity; } // create jpa entity manager EntityManager entityManager = getEntityManager(); // get subscription for (Object object : entityManager.createNamedQuery( Subscription.JPA_NAMED_QUERY_SELECT_SUBSCRIPTION_FROM_TIMERID).setParameter("timerID", event.getTimerID()).getResultList()) { Subscription subscription = (Subscription) object; if (logger.isInfoEnabled()) { logger.info("Timer expired for " + subscription); } ImplementedSubscriptionControlSbbLocalObject childSbb = getImplementedControlChildSbb(); // check subscription status if (subscription.getStatus().equals(Subscription.Status.waiting)) { // change subscription status subscription.changeStatus(Subscription.Event.giveup); if (logger.isInfoEnabled()) { logger.info("Status changed for " + subscription); } // notify winfo subscription(s) getWInfoSubscriptionHandler().notifyWinfoSubscriptions( entityManager, subscription, childSbb); // remove subscription data removeSubscriptionData(entityManager, subscription, dialog, aci, childSbb); } else { subscription.changeStatus(Event.timeout); // remove subscription if (subscription.getResourceList()) { getEventListControlChildSbb().removeSubscription(subscription); } if (dialog != null) { // sip subscription new SipSubscriptionHandler(this) .getRemoveSipSubscriptionHandler() .removeSipSubscription(aci, subscription, entityManager, childSbb); } else { // internal subscription new InternalSubscriptionHandler(this) .getRemoveInternalSubscriptionHandler() .removeInternalSubscription(aci, subscription, entityManager, childSbb); } } entityManager.flush(); } // close entity manager entityManager.close(); } public void onInternalNotifyEvent(InternalNotifyEvent event, ActivityContextInterface aci) { // notify the parent getParentSbbCMP().notifyEvent(event.getSubscriber(), event.getNotifier(), event.getEventPackage(), event.getSubscriptionId(), event.getTerminationReason(), event.getSubscriptionStatus(), event.getContent(), event.getContentType(), event.getContentSubtype()); // if subscription terminated then we remove the null aci if (event.getSubscriptionStatus() .equals(Subscription.Status.terminated)) { ((NullActivity) aci.getActivity()).endActivity(); aci.detach(getSbbContext().getSbbLocalObject()); } } // ----------- SBB LOCAL OBJECT public void newSubscriptionAuthorization(String subscriber, String subscriberDisplayName, String notifier, SubscriptionKey key, int expires, int responseCode, boolean eventList, ServerTransaction serverTransaction) { EntityManager entityManager = getEntityManager(); try { if (!key.isInternalSubscription()) { // sip subscription // look for an aci of this server tx ActivityContextInterface serverTransactionACI = null; for (ActivityContextInterface aci : getSbbContext() .getActivities()) { Object activity = aci.getActivity(); if (activity instanceof ServerTransaction) { serverTransactionACI = aci; break; } } new SipSubscriptionHandler(this).getNewSipSubscriptionHandler() .newSipSubscriptionAuthorization(serverTransaction, serverTransactionACI, subscriber, subscriberDisplayName, notifier, key, expires, responseCode, eventList, entityManager, getImplementedControlChildSbb()); } else { new InternalSubscriptionHandler(this) .getNewInternalSubscriptionHandler() .newInternalSubscriptionAuthorization(subscriber, subscriberDisplayName, notifier, key, expires, responseCode, eventList, entityManager, getImplementedControlChildSbb()); } entityManager.flush(); } catch (Exception e) { logger.error("Error processing new subscription authorization", e); // cleanup if (!key.isInternalSubscription()) { if (serverTransaction != null) { try { Response response = new SipSubscriptionHandler(this) .addContactHeader(messageFactory .createResponse( Response.SERVER_INTERNAL_ERROR, serverTransaction.getRequest())); serverTransaction.sendResponse(response); if (logger.isDebugEnabled()) { logger.debug("Response sent:\n" + response.toString()); } } catch (Exception f) { logger.error("Can't send RESPONSE", f); } } } else { getParentSbbCMP().subscribeError(subscriber, notifier, key.getEventPackage(), key.getRealEventId(), Response.SERVER_INTERNAL_ERROR); } return; } entityManager.close(); } public void notifySubscribers(String notifier, String eventPackage, Object content, ContentTypeHeader contentTypeHeader) { ImplementedSubscriptionControlSbbLocalObject childSbb = getImplementedControlChildSbb(); // create jpa entity manager EntityManager entityManager = getEntityManager(); // process subscriptions for (Object object : entityManager.createNamedQuery( Subscription.JPA_NAMED_QUERY_SELECT_SUBSCRIPTIONS_FROM_NOTIFIER_AND_EVENTPACKAGE).setParameter( "notifier", notifier) .setParameter("eventPackage", eventPackage).getResultList()) { Subscription subscription = (Subscription) object; if (subscription.getStatus().equals(Subscription.Status.active)) { if (subscription.getKey().isInternalSubscription()) { // internal subscription new InternalSubscriptionHandler(this) .getInternalSubscriberNotificationHandler() .notifyInternalSubscriber(entityManager, subscription, content, contentTypeHeader, childSbb); } else { // sip subscription new SipSubscriptionHandler(this) .getSipSubscriberNotificationHandler() .notifySipSubscriber(content, contentTypeHeader, subscription, entityManager, childSbb); } } } // close entity manager entityManager.close(); } public void notifySubscriber(SubscriptionKey key, Object content, ContentTypeHeader contentTypeHeader) { ImplementedSubscriptionControlSbbLocalObject childSbb = getImplementedControlChildSbb(); // create jpa entity manager EntityManager entityManager = getEntityManager(); // get subscription Subscription subscription = entityManager.find(Subscription.class, key); if (subscription != null && subscription.getStatus().equals(Subscription.Status.active)) { if (subscription.getKey().isInternalSubscription()) { // internal subscription new InternalSubscriptionHandler(this) .getInternalSubscriberNotificationHandler() .notifyInternalSubscriber(entityManager, subscription, content, contentTypeHeader, childSbb); } else { // sip subscription new SipSubscriptionHandler(this) .getSipSubscriberNotificationHandler() .notifySipSubscriber(content, contentTypeHeader, subscription, entityManager, childSbb); } } // close entity manager entityManager.close(); } public void authorizationChanged(String subscriber, String notifier, String eventPackage, String eventId, int authorizationCode) { // get entity manager EntityManager entityManager = getEntityManager(); // get this entity dialog (if it's not a internal subscription) and the // subscription aci Dialog dialog = null; ActivityContextInterface subscriptionAci = null; for (ActivityContextInterface aci : sbbContext.getActivities()) { Object activity = aci.getActivity(); if (activity instanceof Dialog) { subscriptionAci = aci; dialog = (Dialog) activity; break; } else if (activity instanceof NullActivity) { subscriptionAci = aci; break; } } // let's find the subscription that matches the parameters String callId = SubscriptionKey.NO_CALL_ID; String remoteTag = SubscriptionKey.NO_REMOTE_TAG; if (dialog != null) { callId = dialog.getCallId().getCallId(); remoteTag = dialog.getRemoteTag(); } Subscription subscription = entityManager.find(Subscription.class, new SubscriptionKey(callId, remoteTag, eventPackage, eventId)); if (subscription != null) { // we have a subscription match Subscription.Status oldStatus = subscription.getStatus(); switch (authorizationCode) { /* * If the <sub-handling> permission changes value to "block", this * causes a "rejected" event to be generated into the subscription * state machine for all affected subscriptions. This will cause the * state machine to move into the "terminated" state, resulting in * the transmission of a NOTIFY to the watcher with a * Subscription-State header field with value "terminated" and a * reason of "rejected" [7], which terminates their subscription. */ case Response.FORBIDDEN: subscription.changeStatus(Subscription.Event.rejected); break; /* * If the <sub-handling> permission changes value to "confirm", the * processing depends on the states of the affected subscriptions. * Unfortunately, the state machine in RFC 3857 does not define an * event corresponding to an authorization decision of "pending". If * the subscription is in the "active" state, it moves back into the * "pending" state. This causes a NOTIFY to be sent, updating the * Subscription-State [7] to "pending". No reason is included in the * Subscription-State header field (none are defined to handle this * case). No further documents are sent to this watcher. There is no * change in state if the subscription is in the "pending", * "waiting", or "terminated" states. */ case Response.ACCEPTED: if (subscription.getStatus().equals(Subscription.Status.active)) { subscription.setStatus(Subscription.Status.pending); subscription.setLastEvent(null); } break; /* * If the <sub-handling> permission changes value from "blocked" or * "confirm" to "polite-block" or "allow", this causes an "approved" * event to be generated into the state machine for all affected * subscriptions. If the subscription was in the "pending" state, * the state machine will move to the "active" state, resulting in * the transmission of a NOTIFY with a Subscription-State header * field of "active", and the inclusion of a presence document in * that NOTIFY. If the subscription was in the "waiting" state, it * will move into the "terminated" state. */ case Response.OK: subscription.changeStatus(Subscription.Event.approved); break; default: logger .warn("Received authorization update with unknown auth code " + authorizationCode); return; } if (!oldStatus.equals(subscription.getStatus())) { // subscription status changed if (logger.isInfoEnabled()) { logger.info("Status changed for " + subscription); } ImplementedSubscriptionControlSbbLocalObject childSbb = getImplementedControlChildSbb(); if (subscription.getStatus().equals( Subscription.Status.terminated)) { if (subscription.getResourceList()) { getEventListControlChildSbb().removeSubscription(subscription); } if (dialog == null) { // internal subscription new InternalSubscriptionHandler(this) .getRemoveInternalSubscriptionHandler() .removeInternalSubscription(subscriptionAci, subscription, entityManager, childSbb); } else { // sip subscription new SipSubscriptionHandler(this) .getRemoveSipSubscriptionHandler() .removeSipSubscription(subscriptionAci, subscription, entityManager, childSbb); } } else { boolean notifySubscriber = true; if (subscription.getResourceList()) { if (subscription.getStatus().equals( Subscription.Status.active)) { notifySubscriber = false; // resource list and subscription now active, create rls subscription if (!getEventListControlChildSbb().createSubscription(subscription)) { if (dialog == null) { // internal subscription new InternalSubscriptionHandler(this) .getRemoveInternalSubscriptionHandler() .removeInternalSubscription(subscriptionAci, subscription, entityManager, childSbb); } else { // sip subscription new SipSubscriptionHandler(this) .getRemoveSipSubscriptionHandler() .removeSipSubscription(subscriptionAci, subscription, entityManager, childSbb); } } } } if (notifySubscriber) { // it's not a resource list or the subscription state is pending // (thus no notify content which means it's // irrelevant to be resource list), notify subscriber if (dialog == null) { // internal subscription new InternalSubscriptionHandler(this) .getInternalSubscriberNotificationHandler() .notifyInternalSubscriber(entityManager, subscription, subscriptionAci, childSbb); } else { // sip subscription try { new SipSubscriptionHandler(this) .getSipSubscriberNotificationHandler() .createAndSendNotify(entityManager, subscription, dialog, childSbb); } catch (Exception e) { logger.error("failed to notify subscriber", e); } } } // notify winfo subscription(s) new WInfoSubscriptionHandler(this).notifyWinfoSubscriptions( entityManager, subscription, childSbb); if (subscription.getStatus().equals( Subscription.Status.waiting)) { // keep the subscription for default waiting time so // notifier may know about this attempt to subscribe // him int defaultWaitingExpires = getConfiguration() .getDefaultWaitingExpires(); // refresh subscription subscription.refresh(defaultWaitingExpires); // set waiting timer setSubscriptionTimerAndPersistSubscription(entityManager, subscription, defaultWaitingExpires + 1, subscriptionAci); } } } } entityManager.flush(); entityManager.close(); } // --- EVENT LIST CALLBACKS public void notifyEventListSubscriber(SubscriptionKey key, MultiPart multiPart) { // notification for subscription on a single resource is no different than resource list try { ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader(MultiPart.MULTIPART_CONTENT_TYPE, MultiPart.MULTIPART_CONTENT_SUBTYPE); contentTypeHeader.setParameter("type", multiPart.getType()); contentTypeHeader.setParameter("boundary", multiPart.getBoundary()); notifySubscriber(key, multiPart.toString(),contentTypeHeader); } catch (ParseException e) { logger.error("failed to create content type header for event list notification", e); } } public Subscription getSubscription(SubscriptionKey key) { EntityManager entityManager = getEntityManager(); Subscription subscription = entityManager.find(Subscription.class, key); if (subscription != null) { subscription.setEntityManager(entityManager); } return subscription; } /* * perhaps there are subscriptions for this uri that are * not set for a rls service, this can happen when a new list is * created and a subscription arrives before the rls knows about the new service * * (non-Javadoc) * @see org.mobicents.slee.sipevent.server.subscription.EventListSubscriptionControlParentSbbLocalObject#rlsServiceUpdated(java.lang.String) */ public void rlsServiceUpdated(String uri) { terminateRlsServiceSubscriptions(uri, Subscription.Event.deactivated); } /* * (non-Javadoc) * @see org.mobicents.slee.sipevent.server.subscription.EventListSubscriptionControlParentSbbLocalObject#rlsServiceRemoved(java.lang.String) */ public void rlsServiceRemoved(String uri) { terminateRlsServiceSubscriptions(uri, Subscription.Event.noresource); } /* * Terminates all subscriptions where the notifier is the specified RLS * service uri, with the provided event. * * @param notifier * * @param event */ private void terminateRlsServiceSubscriptions(String notifier, Subscription.Event event) { if (configuration.getEventListSupportOn()) { // create jpa entity manager EntityManager entityManager = getEntityManager(); int index = notifier.indexOf(';'); String notifierWithoutParams = null; String notifierParams = null; if (index > 0) { notifierWithoutParams = notifier.substring(0,index); notifierParams = notifier.substring(index); } else { notifierWithoutParams = notifier; notifierParams = null; } // process subscriptions for (Object object : entityManager.createNamedQuery( Subscription.JPA_NAMED_QUERY_SELECT_SUBSCRIPTIONS_FROM_NOTIFIER_WITH_PARAMS).setParameter( "notifier", notifierWithoutParams).setParameter("notifierParams", notifierParams).getResultList()) { Subscription subscription = (Subscription) object; if (!subscription.getResourceList() || event == Subscription.Event.noresource) { if (logger.isDebugEnabled()) { logger.debug("terminating rls service "+notifier+" subscription "+subscription+" with event "+event); } // remove subscription in those cases subscription.changeStatus(event); try { // get subscription aci ActivityContextInterface aci = getActivityContextNamingfacility().lookup( subscription.getKey().toString()); if (aci != null) { if (subscription.getKey().isInternalSubscription()) { new InternalSubscriptionHandler(this).getRemoveInternalSubscriptionHandler().removeInternalSubscription(aci, subscription, entityManager, getImplementedControlChildSbb()); } else { new SipSubscriptionHandler(this).getRemoveSipSubscriptionHandler().removeSipSubscription(aci, subscription, entityManager, getImplementedControlChildSbb()); } } } catch (Exception e) { logger.error("failed to notify internal subscriber", e); } } } // close entity manager entityManager.close(); } } // --- INTERNAL SUBSCRIPTIONS private void subscribe(String subscriber, String subscriberDisplayName, String notifier, String eventPackage, String subscriptionId, int expires, String content, String contentType, String contentSubtype, boolean eventList) { EntityManager entityManager = getEntityManager(); getInternalSubscriptionHandler().getNewInternalSubscriptionHandler() .newInternalSubscription(subscriber, subscriberDisplayName, notifier, eventPackage, subscriptionId, expires, content, contentType, contentSubtype, eventList, entityManager, getImplementedControlChildSbb()); entityManager.flush(); entityManager.close(); } public void subscribe(String subscriber, String subscriberDisplayName, String notifier, String eventPackage, String subscriptionId, int expires, String content, String contentType, String contentSubtype) { if (getConfiguration().getEventListSupportOn()) { int rlsResponse = getEventListControlChildSbb().validateSubscribeRequest(subscriber, notifier, eventPackage, null); switch (rlsResponse) { case Response.NOT_FOUND: subscribe(subscriber, subscriberDisplayName, notifier, eventPackage, subscriptionId, expires, content, contentType, contentSubtype, false); break; case Response.OK: subscribe(subscriber, subscriberDisplayName, notifier, eventPackage, subscriptionId, expires, content, contentType, contentSubtype, true); break; default: // rls provided an error while validating event list possible subscribe getParentSbbCMP().subscribeError(subscriber, notifier, eventPackage, subscriptionId, rlsResponse); break; } } else { subscribe(subscriber, subscriberDisplayName, notifier, eventPackage, subscriptionId, expires, content, contentType, contentSubtype, false); } } public void resubscribe(String subscriber, String notifier, String eventPackage, String subscriptionId, int expires) { EntityManager entityManager = getEntityManager(); getInternalSubscriptionHandler() .getRefreshInternalSubscriptionHandler() .refreshInternalSubscription(subscriber, notifier, eventPackage, subscriptionId, expires, entityManager, getImplementedControlChildSbb()); entityManager.flush(); entityManager.close(); } public void unsubscribe(String subscriber, String notifier, String eventPackage, String subscriptionId) { EntityManager entityManager = getEntityManager(); getInternalSubscriptionHandler().getRemoveInternalSubscriptionHandler() .removeInternalSubscription(subscriber, notifier, eventPackage, subscriptionId, entityManager, getImplementedControlChildSbb()); entityManager.flush(); entityManager.close(); } // ----------- AUX METHODS public void setSubscriptionTimerAndPersistSubscription( EntityManager entityManager, Subscription subscription, long delay, ActivityContextInterface aci) { TimerOptions options = new TimerOptions(); options.setPersistent(true); options.setPreserveMissed(TimerPreserveMissed.ALL); // set timer TimerID timerId = timerFacility.setTimer(aci, null, System .currentTimeMillis() + (delay * 1000), 1, 1, options); subscription.setTimerID(timerId); // update subscription entityManager.persist(subscription); } /** * Removes a subscription data. * * @param entityManager * @param subscription * @param dialog * @param aci * @param childSbb */ public void removeSubscriptionData(EntityManager entityManager, Subscription subscription, Dialog dialog, ActivityContextInterface aci, ImplementedSubscriptionControlSbbLocalObject childSbb) { // warn event package impl that subscription is to be removed, may need // to clean up resources childSbb.removingSubscription(subscription); // remove subscription entityManager.remove(subscription); // remove aci name binding try { getActivityContextNamingfacility().unbind( subscription.getKey().toString()); } catch (Exception e) { logger.error("failed to unbind subscription aci name"); } // if dialog is not null that's a sip subscription and we need to verify // if dialog is not needed anymore (and remove if that's the case) if (dialog != null) { // get subscriptions of dialog from persistence List<?> subscriptionsInDialog = Subscription.getDialogSubscriptions( entityManager, dialog.getCallId().getCallId(), dialog .getRemoteTag()); if (subscriptionsInDialog.size() == 0) { if (logger.isInfoEnabled()) { logger.info("No more subscriptions on dialog, deleting..."); } // no more subscriptions in dialog, detach and delete the dialog aci.detach(getSbbContext().getSbbLocalObject()); dialog.delete(); } } // note: we don't remove null acis here, otherwise the final notify // couldn't be handled entityManager.flush(); if (logger.isInfoEnabled()) { logger.info("Removed data for " + subscription); } } // ----------- SBB OBJECT's LIFE CYCLE /** * SbbObject's context setting */ public void setSbbContext(SbbContext sbbContext) { this.sbbContext = sbbContext; // retrieve factories, facilities & providers try { Context context = (Context) new InitialContext() .lookup("java:comp/env"); timerFacility = (TimerFacility) context .lookup("slee/facilities/timer"); nullACIFactory = (NullActivityContextInterfaceFactory) context .lookup("slee/nullactivity/activitycontextinterfacefactory"); nullActivityFactory = (NullActivityFactory) context .lookup("slee/nullactivity/factory"); sipActivityContextInterfaceFactory = (SipActivityContextInterfaceFactory) context .lookup("slee/resources/jainsip/1.2/acifactory"); sipProvider = (SleeSipProvider) context .lookup("slee/resources/jainsip/1.2/provider"); addressFactory = sipProvider.getAddressFactory(); headerFactory = sipProvider.getHeaderFactory(); messageFactory = sipProvider.getMessageFactory(); activityContextNamingfacility = (ActivityContextNamingFacility) context .lookup("slee/facilities/activitycontextnaming"); } catch (Exception e) { logger.error( "Unable to retrieve factories, facilities & providers", e); } } public void sbbActivate() { } public void sbbCreate() throws CreateException { } public void sbbExceptionThrown(Exception arg0, Object arg1, ActivityContextInterface arg2) { } public void sbbLoad() { } public void sbbPassivate() { } public void sbbPostCreate() throws CreateException { } public void sbbRemove() { } public void sbbRolledBack(RolledBackContext arg0) { } public void sbbStore() { } public void unsetSbbContext() { this.sbbContext = null; } /** * Used to fire events to notify the right sbb entity of a state change * * @param event * @param aci * @param address */ public abstract void fireInternalNotifyEvent(InternalNotifyEvent event, ActivityContextInterface aci, Address address); }