package org.mobicents.slee.sipevent.server.subscription.eventlist; import java.util.Set; import gov.nist.javax.sip.Utils; import javax.persistence.EntityManager; import javax.sip.message.Response; import javax.slee.ActivityContextInterface; import javax.slee.ChildRelation; import javax.slee.CreateException; import javax.slee.RolledBackContext; import javax.slee.Sbb; import javax.slee.SbbContext; import org.apache.log4j.Logger; import org.mobicents.slee.sipevent.server.subscription.EventListSubscriberParentSbbLocalObject; import org.mobicents.slee.sipevent.server.subscription.EventListSubscriberSbbLocalObject; import org.mobicents.slee.sipevent.server.subscription.SubscriptionClientControlParentSbbLocalObject; import org.mobicents.slee.sipevent.server.subscription.SubscriptionClientControlSbbLocalObject; 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.pojo.Subscription.Status; import org.openxdm.xcap.client.appusage.resourcelists.jaxb.EntryType; /** * * Sbb that acts as the back end subscriber to the entries in a resource list. * * @author Eduardo Martins * */ public abstract class EventListSubscriberSbb implements Sbb, EventListSubscriberSbbLocalObject { private static final Logger logger = Logger .getLogger(EventListSubscriberSbb.class); // --- CMPs public abstract void setNotificationData(NotificationData value); public abstract NotificationData getNotificationData(); public abstract void setFlatList(String uri); public abstract String getFlatList(); public abstract void setSubscriptionKey(SubscriptionKey subscriptionKey); public abstract SubscriptionKey getSubscriptionKey(); public abstract void setSubscriber(String subscriber); public abstract String getSubscriber(); public abstract void setParentSbbCMP(EventListSubscriberParentSbbLocalObject parentSbb); public abstract EventListSubscriberParentSbbLocalObject getParentSbbCMP(); // --- sbb logic public void setParentSbb(EventListSubscriberParentSbbLocalObject parentSbb) { setParentSbbCMP(parentSbb); } private String getVirtualSubscriptionId(SubscriptionKey originalSubscriptionKey,String virtualSubscriptionNotifier) { return originalSubscriptionKey.toString() + ":list:" +virtualSubscriptionNotifier; } public void subscribe(Subscription subscription, FlatList flatList, ActivityContextInterface flatListACI) { if (logger.isDebugEnabled()) { logger.debug("creating backend subscriptions for rls subscription "+subscription.getKey()); } // store subscription data in cmp setSubscriptionKey(subscription.getKey()); setFlatList(flatList.getServiceType().getUri()); setSubscriber(subscription.getSubscriber()); flatListACI.attach(sbbContext.getSbbLocalObject()); // set notification data object, when a notification comes and this // object exists the notification data will be added, otherwise a new // NotificationData object for a single entry is created and the parent // is notified with the resulting multipart setNotificationData(new NotificationData(subscription.getNotifierWithParams(),subscription.getVersion(),flatList,Utils.getInstance().generateTag(),Utils.getInstance().generateTag())); // get subscription client child SubscriptionClientControlSbbLocalObject subscriptionClient = getSubscriptionClientControlSbb(); // create "virtual" subscriptions for (EntryType entryType : flatList.getEntries().values()) { subscriptionClient.subscribe(subscription.getSubscriber(), subscription.getSubscriberDisplayName(), entryType.getUri(), subscription.getKey().getEventPackage(), getVirtualSubscriptionId(subscription.getKey(),entryType.getUri()), subscription.getExpires(), null, null, null); } } public void onFlatListRemovedEvent(FlatListRemovedEvent event, ActivityContextInterface aci) { if (logger.isDebugEnabled()) { logger.debug("flat list activity ending, terminating subscription "+getSubscriptionKey()); } // time to remove the subscription unsubscribe(getSubscriber(), getSubscriptionKey(),event.getFlatList()); } public void onFlatListUpdatedEvent(FlatListUpdatedEvent event, ActivityContextInterface aci) { Subscription subscription = getParentSbbCMP().getSubscription(getSubscriptionKey()); if (subscription != null) { resubscribe(subscription, event.getFlatList(),event.getNewEntries(),event.getOldEntries(),event.getRemovedEntries()); } } public void resubscribe(Subscription subscription, FlatList flatList) { resubscribe(subscription, flatList, null, flatList.getEntries().keySet(), null); } private void resubscribe(Subscription subscription, FlatList flatList, Set<String> newEntries, Set<String> oldEntries, Set<String> removedEntries) { if (logger.isDebugEnabled()) { logger.debug("refreshing backend subscriptions for rls subscription "+subscription.getKey()); } // version is incremented subscription.incrementVersion(); // prepare for a full state notification setNotificationData(new NotificationData(subscription.getNotifierWithParams(),subscription.getVersion(),flatList,Utils.getInstance().generateTag(),Utils.getInstance().generateTag())); // get subscription client child SubscriptionClientControlSbbLocalObject subscriptionClient = getSubscriptionClientControlSbb(); // update "virtual" subscriptions if (removedEntries != null) { for (String entryUri : removedEntries) { subscriptionClient.unsubscribe(subscription.getSubscriber(), entryUri, subscription.getKey().getEventPackage(), getVirtualSubscriptionId(subscription.getKey(),entryUri)); } } if (oldEntries != null) { for (String entryUri : oldEntries) { subscriptionClient.resubscribe(subscription.getSubscriber(), entryUri, subscription.getKey().getEventPackage(), getVirtualSubscriptionId(subscription.getKey(),entryUri), subscription.getExpires()); } } if (newEntries != null) { for (String entryUri : newEntries) { subscriptionClient.subscribe(subscription.getSubscriber(), subscription.getSubscriberDisplayName(), entryUri, subscription.getKey().getEventPackage(), getVirtualSubscriptionId(subscription.getKey(),entryUri), subscription.getExpires(),null,null,null); } } } public void unsubscribe(Subscription subscription, FlatList flatList) { unsubscribe(subscription.getSubscriber(), subscription.getKey(), flatList); } private void unsubscribe(String subscriber, SubscriptionKey key, FlatList flatList) { if (logger.isDebugEnabled()) { logger.debug("removing backend subscriptions for rls subscription "+key); } for (ActivityContextInterface aci : sbbContext.getActivities()) { aci.detach(sbbContext.getSbbLocalObject()); } // let's set the key as null so there are no further notifications from back end subscriptions setSubscriptionKey(null); // get subscription client child SubscriptionClientControlSbbLocalObject subscriptionClient = getSubscriptionClientControlSbb(); // remove "virtual" subscriptions for (EntryType entryType : flatList.getEntries().values()) { subscriptionClient.unsubscribe(subscriber, entryType.getUri(), key.getEventPackage(), getVirtualSubscriptionId(key,entryType.getUri())); } } private Subscription getSubscription(EventListSubscriberParentSbbLocalObject parentSbb, SubscriptionKey key, String subscriber) { Subscription subscription = parentSbb.getSubscription(key); if (subscription == null && getSubscriptionKey() != null) { logger.warn("Unable to get subscription "+key+" from parent sbb, it does not exists anymore! Removing all virtual subscriptions"); unsubscribe(subscriber, key,parentSbb.getFlatList(getFlatList())); } return subscription; } // --- SIP EVETN CHILD SBB CALL BACKS private NotificationData createPartialStateNotificationData(EventListSubscriberParentSbbLocalObject parentSbb, SubscriptionKey subscriptionKey, String subscriber, String notifier) { // get subscription Subscription subscription = getSubscription(parentSbb, subscriptionKey, subscriber); if (subscription != null) { // increment subscription version subscription.incrementVersion(); EntityManager entityManager = subscription.getEntityManager(); try { entityManager.flush(); entityManager.close(); } catch (Exception e) { if (logger.isDebugEnabled()) { logger.debug("failed to update rls subscription", e); } return null; } if (logger.isDebugEnabled()) { logger.debug("(before partial notification) flat list: "+getFlatList()); } // create notification data for a single resource FlatList flatList = parentSbb.getFlatList(getFlatList()); if (flatList != null) { return new NotificationData(subscription.getNotifierWithParams(),subscription.getVersion(),flatList.getEntries().get(notifier),Utils.getInstance().generateTag(), Utils.getInstance().generateTag()); } } return null; } public void notifyEvent(String subscriber, String notifier, String eventPackage, String subscriptionId, Event terminationReason, Status status, String content, String contentType, String contentSubtype) { SubscriptionKey subscriptionKey = getSubscriptionKey(); // if key is null we are removing subscriptions and have no interest in further notifications if (subscriptionKey != null) { if (logger.isDebugEnabled()) { logger.debug("notification for rls subscription "+subscriptionKey+" from " + notifier); } EventListSubscriberParentSbbLocalObject parentSbb = getParentSbbCMP(); NotificationData notificationData = getNotificationData(); if (notificationData == null) { notificationData = createPartialStateNotificationData(parentSbb, subscriptionKey, subscriber, notifier); if (notificationData == null) { // null then abort notification return; } } // add notification data String id = notifier; String cid = content != null ? id : null; MultiPart multiPart = null; try { multiPart = notificationData.addNotificationData(notifier, cid, id, content, contentType, contentSubtype, status.toString(), (terminationReason == null ? null : terminationReason.toString())); } catch (IllegalStateException e) { // there is a chance that on a full state update concurrent backend subscriptions may try add notification data after multipart was built, if that happens we will get this exception and do a partial notification notificationData = createPartialStateNotificationData(parentSbb, subscriptionKey, subscriber, notifier); if (notificationData == null) { // null then abort notification return; } multiPart = notificationData.addNotificationData(notifier, cid, id, content, contentType, contentSubtype, status.toString(), (terminationReason == null ? null : terminationReason.toString())); } // notify parent? if (multiPart != null) { setNotificationData(null); parentSbb.notifyEventListSubscriber(subscriptionKey, multiPart); } } } public void resubscribeError(String subscriber, String notifier, String eventPackage, String subscriptionId, int error) { if (logger.isDebugEnabled()) { logger.debug("resubscribeError: sid="+subscriptionId+", error="+error); } EventListSubscriberParentSbbLocalObject parentSbb = getParentSbbCMP(); SubscriptionKey key = getSubscriptionKey(); switch (error) { case Response.CONDITIONAL_REQUEST_FAILED: // perhaps virtual subscription died, lets try to subscribe again Subscription subscription = getSubscription(parentSbb, key, subscriber); subscription.getEntityManager().close(); if (subscription != null) { getSubscriptionClientControlSbb().subscribe(subscriber, subscription.getSubscriberDisplayName(), notifier, eventPackage, subscriptionId, subscription.getExpires(), null, null, null); } break; default: break; } } public void resubscribeOk(String subscriber, String notifier, String eventPackage, String subscriptionId, int expires) { // ignore if (logger.isDebugEnabled()) { logger.debug("resubscribeOk: sid="+subscriptionId); } } public void subscribeError(String subscriber, String notifier, String eventPackage, String subscriptionId, int error) { if (logger.isDebugEnabled()) { logger.debug("subscribeError: sid="+subscriptionId+", error="+error); } EventListSubscriberParentSbbLocalObject parentSbb = getParentSbbCMP(); SubscriptionKey subscriptionKey = getSubscriptionKey(); NotificationData notificationData = getNotificationData(); if (notificationData == null) { notificationData = createPartialStateNotificationData(parentSbb, subscriptionKey, subscriber, notifier); } String cid = notifier; MultiPart multiPart = null; // add notification data switch (error) { case Response.FORBIDDEN: try { multiPart = notificationData.addNotificationData(notifier, null, cid, null, null, null, "terminated", "rejected"); } catch (IllegalStateException e) { // there is a chance that on a full state update concurrent backend subscriptions may try add notification data after multipart was built, if that happens we will get this exception and do a partial notification notificationData = createPartialStateNotificationData(parentSbb, subscriptionKey, subscriber, notifier); multiPart = notificationData.addNotificationData(notifier, null, cid, null, null, null, "terminated", "rejected"); } break; default: try { multiPart = notificationData.notificationDataNotNeeded(notifier); } catch (IllegalStateException e) { // there is a chance that on a full state update concurrent backend subscriptions may try add notification data after multipart was built, if that happens we will get this exception and do a partial notification notificationData = createPartialStateNotificationData(parentSbb, subscriptionKey, subscriber, notifier); multiPart = notificationData.notificationDataNotNeeded(notifier); } break; } // notify parent? if (multiPart != null) { setNotificationData(null); parentSbb.notifyEventListSubscriber(subscriptionKey, multiPart); } } public void subscribeOk(String subscriber, String notifier, String eventPackage, String subscriptionId, int expires, int responseCode) { // ignore if (logger.isDebugEnabled()) { logger.debug("subscribeOk: sid="+subscriptionId); } } public void unsubscribeError(String subscriber, String notifier, String eventPackage, String subscriptionId, int error) { if (logger.isDebugEnabled()) { logger.debug("unsubscribeError: sid="+subscriptionId+", error="+error); } } public void unsubscribeOk(String subscriber, String notifier, String eventPackage, String subscriptionId) { if (logger.isDebugEnabled()) { logger.debug("unsubscribeOk: sid="+subscriptionId); } } // --- SIP EVENT CLIENT CHILD SBB public abstract ChildRelation getSubscriptionClientControlChildRelation(); public abstract SubscriptionClientControlSbbLocalObject getSubscriptionClientControlChildSbbCMP(); public abstract void setSubscriptionClientControlChildSbbCMP( SubscriptionClientControlSbbLocalObject value); public SubscriptionClientControlSbbLocalObject getSubscriptionClientControlSbb() { SubscriptionClientControlSbbLocalObject childSbb = getSubscriptionClientControlChildSbbCMP(); if (childSbb == null) { try { childSbb = (SubscriptionClientControlSbbLocalObject) getSubscriptionClientControlChildRelation() .create(); } catch (Exception e) { logger.error("Failed to create child sbb", e); return null; } setSubscriptionClientControlChildSbbCMP(childSbb); childSbb .setParentSbb((SubscriptionClientControlParentSbbLocalObject) this.sbbContext .getSbbLocalObject()); } return childSbb; } // ----------- SBB OBJECT's LIFE CYCLE private SbbContext sbbContext; public void setSbbContext(SbbContext sbbContext) { this.sbbContext = sbbContext; } 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; } }