package org.mobicents.slee.sipevent.server.subscription.eventlist; import java.io.StringReader; import java.util.HashSet; import java.util.Iterator; import java.util.ListIterator; import java.util.Map; import java.util.Set; import javax.naming.Context; import javax.naming.InitialContext; import javax.sip.RequestEvent; import javax.sip.header.AcceptHeader; import javax.sip.header.SupportedHeader; import javax.sip.message.Response; import javax.slee.ActivityContextInterface; 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.nullactivity.NullActivity; import javax.slee.nullactivity.NullActivityContextInterfaceFactory; import javax.slee.nullactivity.NullActivityFactory; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; 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.EventListSubscriptionControlParentSbbLocalObject; import org.mobicents.slee.sipevent.server.subscription.EventListSubscriptionControlSbbLocalObject; import org.mobicents.slee.sipevent.server.subscription.FlatListMakerParentSbbLocalObject; import org.mobicents.slee.sipevent.server.subscription.FlatListMakerSbbLocalObject; import org.mobicents.slee.sipevent.server.subscription.pojo.Subscription; import org.mobicents.slee.sipevent.server.subscription.pojo.SubscriptionKey; import org.mobicents.slee.xdm.server.XDMClientControlParentSbbLocalObject; import org.mobicents.slee.xdm.server.XDMClientControlSbbLocalObject; import org.openxdm.xcap.client.appusage.rlsservices.jaxb.RlsServices; import org.openxdm.xcap.client.appusage.rlsservices.jaxb.ServiceType; import org.openxdm.xcap.common.key.GlobalDocumentUriKey; import org.openxdm.xcap.common.key.XcapUriKey; import org.openxdm.xcap.common.uri.AttributeSelector; import org.openxdm.xcap.common.uri.DocumentSelector; import org.openxdm.xcap.common.uri.NodeSelector; /** * * * @author Eduardo Martins * */ public abstract class EventListSubscriptionControlSbb implements Sbb, EventListSubscriptionControlSbbLocalObject { private static final Logger logger = Logger .getLogger(EventListSubscriptionControlSbb.class); /** * caching rls-services of xdm */ private static final RlsServicesCache rlsServicesCache = new RlsServicesCache(); /** * key that points to the global index document of rls-services in the xdm * server */ private static final GlobalDocumentUriKey globalRLSDocumentKey = new GlobalDocumentUriKey( "rls-services", "index"); /** * can verify if a service type object has a certain event package */ private static final ServiceTypePackageVerifier serviceTypePackageVerifier = new ServiceTypePackageVerifier(); // --- parent sbb public abstract void setParentSbbCMP( EventListSubscriptionControlParentSbbLocalObject sbbLocalObject); public abstract EventListSubscriptionControlParentSbbLocalObject getParentSbbCMP(); public void setParentSbb( EventListSubscriptionControlParentSbbLocalObject sbbLocalObject) { setParentSbbCMP(sbbLocalObject); } // --- rls management of cached rls-services public void initRLSCache() { if (logger.isInfoEnabled()) { logger .info("Mobicents Resource List Server: starting cache of XDM's global rls-services document."); } XDMClientControlSbbLocalObject xdm = getXDMClientControlSbb(); // ask for the global document xdm.get(globalRLSDocumentKey, null); // subscribe to get notifications on updates xdm.subscribeDocument(globalRLSDocumentKey.getDocumentSelector()); } public void shutdownRLSCache() { removeFlatLists(rlsServicesCache.getFlatListServiceURIs()); getXDMClientControlSbb().unsubscribeDocument(globalRLSDocumentKey.getDocumentSelector()); } private String getRLSServiceACIName(String serviceURI) { return "rls:aci:"+serviceURI; } private void createRLSServiceACI(String serviceURI) { NullActivity nullActivity = nullActivityFactory.createNullActivity(); try { ActivityContextInterface aci = nullACIFactory.getActivityContextInterface(nullActivity); activityContextNamingfacility.bind(aci, getRLSServiceACIName(serviceURI)); } catch (Exception e) { logger.error(e.getMessage(),e); } } private ActivityContextInterface getRLSServiceACI(String serviceURI) { try { return activityContextNamingfacility.lookup(getRLSServiceACIName(serviceURI)); } catch (Exception e) { logger.error(e.getMessage(),e); return null; } } private ActivityContextInterface removeRLSServiceACI(String serviceURI) { try { ActivityContextInterface aci = getRLSServiceACI(serviceURI); activityContextNamingfacility.unbind(getRLSServiceACIName(serviceURI)); ((NullActivity)aci.getActivity()).endActivity(); return aci; } catch (Exception e) { logger.error(e.getMessage(),e); return null; } } private void updateCache(String document) { if (logger.isDebugEnabled()) { logger.debug("Mobicents Resource List Server: updating cache..."); } Set<String> servicesToRemove = new HashSet<String>(rlsServicesCache.getFlatListServiceURIs()); StringReader stringReader = new StringReader(document); try { Unmarshaller unmarshaller = context.createUnmarshaller(); Object obj = unmarshaller.unmarshal(stringReader); if (obj instanceof RlsServices) { for (ServiceType serviceType : ((RlsServices) obj).getService()) { if (!servicesToRemove.remove(serviceType.getUri())) { // new flat list createRLSServiceACI(serviceType.getUri()); } getFlatListMakerSbb().makeFlatList(serviceType); } } } catch (Exception e) { logger.error("failed to unmarshall rls services content", e); } finally { stringReader.close(); } removeFlatLists(servicesToRemove); } private void removeFlatLists(Set<String> servicesToRemove) { if (!servicesToRemove.isEmpty()) { XDMClientControlSbbLocalObject xdmClientSbb = getXDMClientControlSbb(); for (String serviceToRemove : servicesToRemove) { FlatList flatList = rlsServicesCache.removeFlatList(serviceToRemove, xdmClientSbb); if (flatList != null) { getParentSbbCMP().rlsServiceRemoved(serviceToRemove); // end rls service aci ActivityContextInterface aci = removeRLSServiceACI(serviceToRemove); // fire event signaling removal fireFlatListRemovedEvent(new FlatListRemovedEvent(flatList),aci,null); } } } } public void flatListMade(FlatList flatList) { if (logger.isInfoEnabled()) { logger.info("Mobicents Resource List Server: updated cache with " + flatList); } XDMClientControlSbbLocalObject xdmClientSbb = getXDMClientControlSbb(); FlatListUpdatedEvent event = rlsServicesCache.putFlatList(flatList,xdmClientSbb); // warn the parent sbb, perhaps there are subscriptions for this flat // list uri that are // not set for a rls service, this can happen when a new list is // created and a subscription arrives before the rlscache is updated getParentSbbCMP().rlsServiceUpdated(flatList.getServiceType().getUri()); if (event != null) { // fire event to warn falt list subscribers about update fireFlatListUpdatedEvent(event, getRLSServiceACI(flatList.getServiceType().getUri()), null); } } public FlatList getFlatList(String serviceUri) { return rlsServicesCache.getFlatList(serviceUri); } public void getResponse(XcapUriKey key, int responseCode, String mimetype, String content, String tag) { if (logger.isDebugEnabled()) { logger .debug("Got " + responseCode + " response for retreival of global rls services document."); } if (responseCode == 200) { // the global doc of rls-services in xdm was updated, update our // cache updateCache(content); } } public void documentUpdated(DocumentSelector documentSelector, String oldETag, String newETag, String documentAsString) { if (documentSelector.getAUID().equals("resource-lists")) { for (FlatList flatList : rlsServicesCache.getFlatListsLinkedToResourceList(documentSelector)) { // rebuild flat list // FIXME flat list should instead just be updated getFlatListMakerSbb().makeFlatList(flatList.getServiceType()); } } else { // rls-services global doc updateCache(documentAsString); } } public void attributeUpdated(DocumentSelector documentSelector, NodeSelector nodeSelector, AttributeSelector attributeSelector, Map<String, String> namespaces, String oldETag, String newETag, String documentAsString, String attributeValue) { // FIXME for now redirect to document updated documentUpdated(documentSelector, oldETag, newETag, documentAsString); } public void elementUpdated(DocumentSelector documentSelector, NodeSelector nodeSelector, Map<String, String> namespaces, String oldETag, String newETag, String documentAsString, String elementAsString) { // FIXME for now redirect to document updated documentUpdated(documentSelector, oldETag, newETag, documentAsString); } // --- rls logic public int validateSubscribeRequest(String subscriber, String notifier, String eventPackage, RequestEvent event) { FlatList flatList = rlsServicesCache.getFlatList(notifier); if (flatList != null) { if (logger.isDebugEnabled()) { logger.debug(notifier + " is a resource list."); } if (event != null) { // check event list support is present in UA boolean isEventListSupported = false; for (ListIterator<?> lit = event.getRequest().getHeaders( SupportedHeader.NAME); lit.hasNext();) { SupportedHeader sh = (SupportedHeader) lit.next(); if (sh.getOptionTag().equals("eventlist")) { isEventListSupported = true; break; } } if (!isEventListSupported) { if (logger.isInfoEnabled()) { logger .info("SIP subscription request for resource list doesn't included Supported: eventlist header"); } return Response.EXTENSION_REQUIRED; } boolean isMultipartAccepted = false; boolean isRlmiAccepted = false; for (ListIterator<?> lit = event.getRequest().getHeaders( AcceptHeader.NAME); lit.hasNext();) { AcceptHeader ah = (AcceptHeader) lit.next(); if (ah.allowsAllContentTypes() && ah.allowsAllContentSubTypes()) { isMultipartAccepted = true; isRlmiAccepted = true; break; } if (!isMultipartAccepted && ah.getContentSubType().equals("related") && ah.getContentType().equals("multipart")) { isMultipartAccepted = true; } if (!isRlmiAccepted && ah.getContentSubType().equals("rlmi+xml") && ah.getContentType().equals("application")) { isRlmiAccepted = true; } } if (!isMultipartAccepted || !isRlmiAccepted) { if (logger.isInfoEnabled()) { logger .info("SIP subscription request for resource list doesn't included proper Accept headers"); } return Response.NOT_ACCEPTABLE; } } // check service's packages contains provided event package if (!serviceTypePackageVerifier.hasPackage(flatList .getServiceType(), eventPackage)) { if (logger.isInfoEnabled()) { logger.info("Resource list " + notifier + " doesn't applies to event package " + eventPackage); } return Response.BAD_EVENT; } if (flatList.getEntries().isEmpty() && flatList.getStatus() != Response.OK) { // we got an error flattening the list and there are no entries // so lets make the subscription fail if (logger.isInfoEnabled()) { logger .info("Resource list " + notifier + " can't be subscribed due to failure in making flat list"); } return flatList.getStatus(); } else { // it is a subscribe for a resource list and it is ok (note: the // flat list may had errors, but it's not empty so we let it // proceed if (logger.isDebugEnabled()) { logger.debug("Resource list " + notifier + " subscription request validated with sucess."); } return Response.OK; } } else { // no resource list found if (logger.isDebugEnabled()) { logger.debug(notifier + " is not a resource list."); } return Response.NOT_FOUND; } } public boolean createSubscription(Subscription subscription) { // get flat list FlatList flatList = rlsServicesCache.getFlatList(subscription .getNotifierWithParams()); if (flatList == null) { return false; } // now create a event list subscriber child sbb EventListSubscriberSbbLocalObject subscriptionChildSbb = null; try { subscriptionChildSbb = (EventListSubscriberSbbLocalObject) getEventListSubscriberChildRelation() .create(); subscriptionChildSbb .setParentSbb((EventListSubscriberParentSbbLocalObject) this.sbbContext .getSbbLocalObject()); } catch (Exception e) { logger.error("Failed to create child sbb", e); return false; } // give the child control over the subscription subscriptionChildSbb.subscribe(subscription, flatList, getRLSServiceACI(flatList.getServiceType().getUri())); return true; } public void refreshSubscription(Subscription subscription) { EventListSubscriberSbbLocalObject childSbb = getEventListSubscriberSbb(subscription .getKey()); if (childSbb != null) { childSbb.resubscribe(subscription,rlsServicesCache.getFlatList(subscription .getNotifierWithParams())); } else { logger .warn("trying to refresh a event list subscription but child sbb not found"); } } public void removeSubscription(Subscription subscription) { EventListSubscriberSbbLocalObject childSbb = getEventListSubscriberSbb(subscription .getKey()); if (childSbb != null) { childSbb.unsubscribe(subscription,rlsServicesCache.getFlatList(subscription.getNotifierWithParams())); } else { logger .warn("trying to unsubscribe a event list subscription but child sbb not found"); } } public void notifyEventListSubscriber(SubscriptionKey key, MultiPart multiPart) { if (logger.isDebugEnabled()) { logger.debug("notifying event list subscription " + key); } // just send to parent getParentSbbCMP().notifyEventListSubscriber(key, multiPart); } public Subscription getSubscription(SubscriptionKey key) { // just send to parent return getParentSbbCMP().getSubscription(key); } // --- FLAT LIST MAKER SBB public abstract ChildRelation getFlatListMakerChildRelation(); public FlatListMakerSbbLocalObject getFlatListMakerSbb() { try { FlatListMakerSbbLocalObject childSbb = (FlatListMakerSbbLocalObject) getFlatListMakerChildRelation() .create(); childSbb.setParentSbb((FlatListMakerParentSbbLocalObject) sbbContext.getSbbLocalObject()); return childSbb; } catch (Exception e) { logger.error("Failed to create child sbb", e); return null; } } // --- EVENT LIST SUBSCRIBER SBB public abstract ChildRelation getEventListSubscriberChildRelation(); public EventListSubscriberSbbLocalObject getEventListSubscriberSbb( SubscriptionKey subscriptionKey) { ChildRelation childRelation = getEventListSubscriberChildRelation(); EventListSubscriberSbbLocalObject subscriptionChildSbb = null; // let's see if child is already created for (Iterator<?> it = childRelation.iterator(); it.hasNext();) { EventListSubscriberSbbLocalObject childSbb = (EventListSubscriberSbbLocalObject) it .next(); SubscriptionKey childSbbSubscriptionKey = childSbb .getSubscriptionKey(); if (childSbbSubscriptionKey != null && childSbbSubscriptionKey.equals(subscriptionKey)) { subscriptionChildSbb = childSbb; break; } } return subscriptionChildSbb; } // --- XDM CLIENT CHILD SBB public abstract ChildRelation getXDMClientControlChildRelation(); public abstract XDMClientControlSbbLocalObject getXDMClientControlChildSbbCMP(); public abstract void setXDMClientControlChildSbbCMP( XDMClientControlSbbLocalObject value); public XDMClientControlSbbLocalObject getXDMClientControlSbb() { XDMClientControlSbbLocalObject childSbb = getXDMClientControlChildSbbCMP(); if (childSbb == null) { try { childSbb = (XDMClientControlSbbLocalObject) getXDMClientControlChildRelation() .create(); } catch (Exception e) { logger.error("Failed to create child sbb", e); return null; } setXDMClientControlChildSbbCMP(childSbb); childSbb .setParentSbb((XDMClientControlParentSbbLocalObject) this.sbbContext .getSbbLocalObject()); } return childSbb; } // --- JAXB private static final JAXBContext context = initJAXBContext(); private static JAXBContext initJAXBContext() { try { return JAXBContext .newInstance("org.openxdm.xcap.client.appusage.rlsservices.jaxb"); } catch (JAXBException e) { logger.error("failed to create jaxb context for rls services", e); return null; } } // ----------- SBB OBJECT's LIFE CYCLE private SbbContext sbbContext; /** * SLEE Facilities */ private ActivityContextNamingFacility activityContextNamingfacility; private NullActivityContextInterfaceFactory nullACIFactory; private NullActivityFactory nullActivityFactory; public void setSbbContext(SbbContext sbbContext) { this.sbbContext = sbbContext; // retrieve factories, facilities & providers try { Context context = (Context) new InitialContext() .lookup("java:comp/env"); nullACIFactory = (NullActivityContextInterfaceFactory) context .lookup("slee/nullactivity/activitycontextinterfacefactory"); nullActivityFactory = (NullActivityFactory) context .lookup("slee/nullactivity/factory"); 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; } // events public abstract void fireFlatListUpdatedEvent(FlatListUpdatedEvent event, ActivityContextInterface aci, Address address); public abstract void fireFlatListRemovedEvent(FlatListRemovedEvent event, ActivityContextInterface aci, Address address); }