/** * Copyright (c) 2011, SOCIETIES Consortium (WATERFORD INSTITUTE OF TECHNOLOGY (TSSG), HERIOT-WATT UNIVERSITY (HWU), SOLUTA.NET * (SN), GERMAN AEROSPACE CENTRE (Deutsches Zentrum fuer Luft- und Raumfahrt e.V.) (DLR), Zavod za varnostne tehnologije * informacijske družbe in elektronsko poslovanje (SETCCE), INSTITUTE OF COMMUNICATION AND COMPUTER SYSTEMS (ICCS), LAKE * COMMUNICATIONS (LAKE), INTEL PERFORMANCE LEARNING SOLUTIONS LTD (INTEL), PORTUGAL TELECOM INOVAÇÃO, SA (PTIN), IBM Corp., * INSTITUT TELECOM (ITSUD), AMITEC DIACHYTI EFYIA PLIROFORIKI KAI EPIKINONIES ETERIA PERIORISMENIS EFTHINIS (AMITEC), TELECOM * ITALIA S.p.a.(TI), TRIALOG (TRIALOG), Stiftelsen SINTEF (SINTEF), NEC EUROPE LTD (NEC)) * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.societies.context.event.impl; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.societies.api.comm.xmpp.interfaces.ICommManager; import org.societies.api.comm.xmpp.pubsub.PubsubClient; import org.societies.api.comm.xmpp.pubsub.Subscriber; import org.societies.api.context.CtxException; import org.societies.api.context.event.CtxChangeEvent; import org.societies.api.context.event.CtxChangeEventListener; import org.societies.api.context.event.CtxEvent; import org.societies.api.context.model.CtxEntityIdentifier; import org.societies.api.context.model.CtxIdentifier; import org.societies.api.context.model.CtxIdentifierFactory; import org.societies.api.context.model.MalformedCtxIdentifierException; import org.societies.api.identity.IIdentity; import org.societies.api.identity.InvalidFormatException; import org.societies.api.osgi.event.CSSEvent; import org.societies.api.osgi.event.CSSEventConstants; import org.societies.api.osgi.event.EMSException; import org.societies.api.osgi.event.EventListener; import org.societies.api.osgi.event.IEventMgr; import org.societies.api.osgi.event.InternalEvent; import org.societies.api.schema.context.contextmanagement.CtxChangeEventBean; import org.societies.context.api.event.CtxChangeEventTopic; import org.societies.context.api.event.CtxEventScope; import org.societies.context.api.event.ICtxEventMgr; import org.societies.context.event.api.CtxEventMgrException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; /** * Implementation of the {@link ICtxEventMgr} interface. * * @author <a href="mailto:nicolas.liampotis@cn.ntua.gr">Nicolas Liampotis</a> (ICCS) * @since 0.0.4 */ @Service("ctxEventMgr") @Lazy(false) public class CtxEventMgr implements ICtxEventMgr { /** The logging facility. */ private static final Logger LOG = LoggerFactory.getLogger(CtxEventMgr.class); private static final List<String> EVENT_SCHEMA_CLASSES = Collections.unmodifiableList(Arrays.asList( "org.societies.api.schema.context.contextmanagement.CtxChangeEventBean")); /** The Event Mgr service. */ @Autowired(required=true) private IEventMgr eventMgr; /** The PubsubClient service reference. */ private PubsubClient pubsubClient; @Autowired(required=true) private ICommManager commMgr; private final Set<LocalChangeEventHandler> localHandlers = new CopyOnWriteArraySet<LocalChangeEventHandler>(); private final ExecutorService localDispatchingService = Executors.newSingleThreadExecutor(); private final ExecutorService remoteDispatchingService = Executors.newSingleThreadExecutor(); @Autowired(required=true) CtxEventMgr(PubsubClient pubsubClient) throws Exception { if (LOG.isInfoEnabled()) LOG.info(this.getClass() + " instantiated"); this.pubsubClient = pubsubClient; try { if (LOG.isDebugEnabled()) LOG.debug("Adding remote context event payload classes '" + EVENT_SCHEMA_CLASSES + "'"); this.pubsubClient.addSimpleClasses(EVENT_SCHEMA_CLASSES); } catch (Exception e) { LOG.error(this.getClass() + " could not be instantiated: " + e.getLocalizedMessage(), e); throw e; } } /* * @see org.societies.context.api.event.ICtxEventMgr#post(org.societies.api.context.event.CtxEvent, java.lang.String[], org.societies.context.api.event.CtxEventScope) */ @Override public void post(final CtxEvent event, final String[] topics, final CtxEventScope scope) { if (event == null) throw new NullPointerException("event can't be null"); if (topics == null) throw new NullPointerException("topics can't be null"); if (scope == null) throw new NullPointerException("scope can't be null"); if (LOG.isDebugEnabled()) LOG.debug("Posting context event '" + event + "' to topics '" + Arrays.toString(topics) + "' with scope " + scope); if (event instanceof CtxChangeEvent) { switch (scope) { case LOCAL: this.localDispatchingService.execute(new LocalChangeEventDispatcher( (CtxChangeEvent) event, topics)); break; case INTRA_CSS: // TODO Handle intra-CSS event publishing break; case INTER_CSS: this.remoteDispatchingService.execute(new RemoteChangeEventDispatcher( (CtxChangeEvent) event, topics)); break; case BROADCAST: this.localDispatchingService.execute(new LocalChangeEventDispatcher( (CtxChangeEvent) event, topics)); this.remoteDispatchingService.execute(new RemoteChangeEventDispatcher( (CtxChangeEvent) event, topics)); break; default: LOG.error("Cannot post event to topics " + Arrays.toString(topics) + ": Unsupported CtxEventScope: " + scope); } } else { LOG.error("Cannot post event to topics " + Arrays.toString(topics) + ": Unsupported CtxEvent implementation"); } } /* * @see org.societies.context.api.event.ICtxEventMgr#registerChangeListener(org.societies.api.context.event.CtxChangeEventListener, java.lang.String[], org.societies.api.identity.IIdentity) */ @Override public void registerChangeListener(final CtxChangeEventListener listener, final String[] topics, final IIdentity ownerId) throws CtxException { if (listener == null) throw new NullPointerException("listener can't be null"); if (topics == null) throw new NullPointerException("topics can't be null"); if (ownerId == null) throw new NullPointerException("ownerId can't be null"); if (this.commMgr.getIdManager().isMine(ownerId)) { // local final String filter = "(" + CSSEventConstants.EVENT_NAME + "=*" + ownerId.toString() + "*)"; this.registerLocalChangeListener(listener, topics, filter); } else { // remote // TODO ? this.registerRemoteChangeListener(ownerId, listener, topics); } } /* * @see org.societies.context.api.event.ICtxEventMgr#unregisterChangeListener(org.societies.api.context.event.CtxChangeEventListener, java.lang.String[], org.societies.api.identity.IIdentity) */ @Override public void unregisterChangeListener(final CtxChangeEventListener listener, final String[] topics, final IIdentity ownerId) throws CtxException { if (listener == null) throw new NullPointerException("listener can't be null"); if (topics == null) throw new NullPointerException("topics can't be null"); if (ownerId == null) throw new NullPointerException("ownerId can't be null"); if (this.commMgr.getIdManager().isMine(ownerId)) { // local final String filter = "(" + CSSEventConstants.EVENT_NAME + "=*" + ownerId.toString() + "*)"; this.unregisterLocalChangeListener(listener, topics, filter); } else { // remote // TODO ? } } /* * @see org.societies.context.api.event.ICtxEventMgr#registerChangeListener(org.societies.api.context.event.CtxChangeEventListener, java.lang.String[], org.societies.api.context.model.CtxIdentifier) */ @Override public void registerChangeListener(final CtxChangeEventListener listener, final String[] topics, final CtxIdentifier ctxId) throws CtxException { if (listener == null) throw new NullPointerException("listener can't be null"); if (topics == null) throw new NullPointerException("topics can't be null"); if (ctxId == null) throw new NullPointerException("ctxId can't be null"); try { final IIdentity pubsubId = this.commMgr.getIdManager().fromJid(ctxId.getOwnerId()); if (this.commMgr.getIdManager().isMine(pubsubId)) { // local final String filter = "(" + CSSEventConstants.EVENT_NAME + "=" + ctxId + ")"; this.registerLocalChangeListener(listener, topics, filter); } else { // remote final String filter = ctxId.toString(); this.registerRemoteChangeListener(pubsubId, listener, topics, filter); } } catch (InvalidFormatException ife) { throw new CtxEventMgrException("Could not register context change event listener: " + "ctxId is not a valid IIdentity: " + ife.getLocalizedMessage(), ife); } } /* * @see org.societies.context.api.event.ICtxEventMgr#unregisterChangeListener(org.societies.api.context.event.CtxChangeEventListener, java.lang.String[], org.societies.api.context.model.CtxIdentifier) */ @Override public void unregisterChangeListener(final CtxChangeEventListener listener, final String[] topics, final CtxIdentifier ctxId) throws CtxException { if (listener == null) throw new NullPointerException("listener can't be null"); if (topics == null) throw new NullPointerException("topics can't be null"); if (ctxId == null) throw new NullPointerException("ctxId can't be null"); try { final IIdentity pubsubId = this.commMgr.getIdManager().fromJid(ctxId.getOwnerId()); if (this.commMgr.getIdManager().isMine(pubsubId)) { // local final String filter = "(" + CSSEventConstants.EVENT_NAME + "=" + ctxId + ")"; this.unregisterLocalChangeListener(listener, topics, filter); } else { // remote // TODO //this.unregisterRemoteChangeListener(pubsubId, listener, topics, ctxId); } } catch (InvalidFormatException ife) { throw new CtxEventMgrException("Could not unregister context change event listener: " + "ctxId is not a valid IIdentity: " + ife.getLocalizedMessage(), ife); } } /* * @see org.societies.context.api.event.ICtxEventMgr#registerChangeListener(org.societies.api.context.event.CtxChangeEventListener, java.lang.String[], org.societies.api.context.model.CtxEntityIdentifier, java.lang.String) */ @Override public void registerChangeListener(final CtxChangeEventListener listener, final String[] topics, final CtxEntityIdentifier scope, final String attrType) throws CtxException { if (listener == null) throw new NullPointerException("listener can't be null"); if (topics == null) throw new NullPointerException("topics can't be null"); if (scope == null) throw new NullPointerException("scope can't be null"); try { final IIdentity pubsubId = this.commMgr.getIdManager().fromJid(scope.getOwnerId()); if (this.commMgr.getIdManager().isMine(pubsubId)) { // local final StringBuilder eventFilterSB = new StringBuilder(); eventFilterSB.append("("); eventFilterSB.append(CSSEventConstants.EVENT_NAME + "=" + scope + "/ATTRIBUTE/"); if (attrType != null) eventFilterSB.append(attrType + "/*"); else eventFilterSB.append("*"); eventFilterSB.append(")"); this.registerLocalChangeListener(listener, topics, eventFilterSB.toString()); } else { // remote final StringBuilder eventFilterSB = new StringBuilder(); eventFilterSB.append(scope.toString() + "/ATTRIBUTE"); if (attrType != null) eventFilterSB.append("/" + attrType); eventFilterSB.append("/\\S+"); this.registerRemoteChangeListener(pubsubId, listener, topics, eventFilterSB.toString()); } } catch (InvalidFormatException ife) { throw new CtxEventMgrException("Could not register context change event listener: " + "ctxId is not a valid IIdentity: " + ife.getLocalizedMessage(), ife); } } /* * @see org.societies.context.api.event.ICtxEventMgr#unregisterChangeListener(org.societies.api.context.event.CtxChangeEventListener, java.lang.String[], org.societies.api.context.model.CtxEntityIdentifier, java.lang.String) */ @Override public void unregisterChangeListener(final CtxChangeEventListener listener, final String[] topics, final CtxEntityIdentifier scope, final String attrType) throws CtxException { if (listener == null) throw new NullPointerException("listener can't be null"); if (topics == null) throw new NullPointerException("topics can't be null"); if (scope == null) throw new NullPointerException("scope can't be null"); try { final IIdentity pubsubId = this.commMgr.getIdManager().fromJid(scope.getOwnerId()); if (this.commMgr.getIdManager().isMine(pubsubId)) { // local final StringBuilder eventFilterSB = new StringBuilder(); eventFilterSB.append("("); eventFilterSB.append(CSSEventConstants.EVENT_NAME + "=" + scope + "/ATTRIBUTE/"); if (attrType != null) eventFilterSB.append(attrType + "/*"); else eventFilterSB.append("*"); eventFilterSB.append(")"); this.unregisterLocalChangeListener(listener, topics, eventFilterSB.toString()); } else { // remote // TODO } } catch (InvalidFormatException ife) { throw new CtxEventMgrException("Could not register context change event listener: " + "ctxId is not a valid IIdentity: " + ife.getLocalizedMessage(), ife); } } /* * @see org.societies.context.api.event.ICtxEventMgr#createTopics(org.societies.api.identity.IIdentity, java.lang.String[]) */ @Override public void createTopics(final IIdentity ownerId, final String[] topics) throws CtxException { final List<String> existingTopics; try { existingTopics = this.pubsubClient.discoItems(ownerId, null); } catch (Exception e) { throw new CtxEventMgrException("Failed to discover topics for IIdentity " + ownerId + ": " + e.getLocalizedMessage(), e); } for (int i = 0; i < topics.length; ++i) { final String topic = topics[i]; if (existingTopics == null || !existingTopics.contains(topic)) { if (LOG.isInfoEnabled()) LOG.info("Creating pubsub node '" + topic + "' for IIdentity " + ownerId); try { this.pubsubClient.ownerCreate(ownerId, topic); } catch (Exception e) { throw new CtxEventMgrException("Failed to create topic '" + topic + "' for IIdentity " + ownerId + ": " + e.getLocalizedMessage(), e); } } else { if (LOG.isInfoEnabled()) LOG.info("Found pubsub node '" + topic + "' for IIdentity " + ownerId); } } } private class LocalChangeEventDispatcher implements Runnable { private final CtxChangeEvent event; private final String[] topics; private LocalChangeEventDispatcher(CtxChangeEvent event, String[] topics) { this.event = event; this.topics = topics; } /* * @see java.lang.Runnable#run() */ @Override public void run() { if (LOG.isDebugEnabled()) LOG.debug("Posting local context change event '" + this.event + "' to topics '" + Arrays.toString(this.topics) + "'"); for (int i = 0; i < this.topics.length; ++i) { final InternalEvent internalEvent = new InternalEvent( this.topics[i], this.event.getId().toString(), this.event.getId().toString(), this.event.getId()); if (LOG.isDebugEnabled()) LOG.debug("Posting local context change event to topic '" + this.topics[i] + "'" + " with internal event name '" + internalEvent.geteventName() + "'"); try { if (eventMgr == null) { LOG.error("Could not post local context change event to topic '" + this.topics[i] + "' with internal event name '" + internalEvent.geteventName() + "'': IEventMgr service is not available"); return; } eventMgr.publishInternalEvent(internalEvent); } catch (EMSException emse) { LOG.error("Could not post local context change event to topic '" + topics[i] + "' with internal event name '" + internalEvent.geteventName() + "': " + emse.getLocalizedMessage(), emse); } } } } private class RemoteChangeEventDispatcher implements Runnable { private final CtxChangeEvent event; private final String[] topics; private RemoteChangeEventDispatcher(CtxChangeEvent event, String[] topics) { this.event = event; this.topics = topics; } /* * @see java.lang.Runnable#run() */ @Override public void run() { if (LOG.isDebugEnabled()) LOG.debug("Posting remote context change event '" + this.event + "' to topics '" + Arrays.toString(this.topics) + "'"); final IIdentity pubsubId; final String itemId; final CtxChangeEventBean eventBean; try { pubsubId = commMgr.getIdManager().fromJid( this.event.getId().getOwnerId()); itemId = this.event.getId().toString(); eventBean = new CtxChangeEventBean(); eventBean.setId(itemId); } catch (Exception e) { LOG.error("Could not post remote context change event '" + this.event + "' to topics '" + Arrays.toString(this.topics) + "': " + e.getLocalizedMessage(), e); return; } for (int i = 0; i < this.topics.length; ++i) { if (LOG.isDebugEnabled()) LOG.debug("Posting remote context change event to topic '" + this.topics[i] + "'" + " with itemId '" + itemId + "'"); try { if (pubsubClient == null) { LOG.error("Could not post remote context change event to topic '" + topics[i] + "': PubsubClient service is not available"); return; } pubsubClient.publisherPublish(pubsubId, topics[i], itemId, eventBean); } catch (Exception e) { LOG.error("Could not post remote context change event to topic '" + topics[i] + "'" + " with itemId '" + itemId + "': " + e.getLocalizedMessage(), e); } } } } private void registerLocalChangeListener(final CtxChangeEventListener listener, final String[] topics, final String filter) throws CtxException { if (LOG.isInfoEnabled()) LOG.info("Registering local context change event listener to topics " + Arrays.toString(topics) + " with filter '" + filter + "'"); final LocalChangeEventHandler localHandler = new LocalChangeEventHandler(listener, filter); if (LOG.isDebugEnabled()) LOG.debug("localHandlers size before register: " + this.localHandlers.size()); if (!this.localHandlers.add(localHandler)) throw new CtxEventMgrException( "Could not register local context change event listener to topics " + Arrays.toString(topics) + " with filter '" + filter + "': Listener already registered"); if (LOG.isDebugEnabled()) LOG.debug("localHandlers size after register: " + this.localHandlers.size()); this.eventMgr.subscribeInternalEvent(localHandler, topics, filter); } private void unregisterLocalChangeListener(final CtxChangeEventListener listener, final String[] topics, final String filter) throws CtxException { if (LOG.isInfoEnabled()) LOG.info("Unregistering local context change event listener from topics " + Arrays.toString(topics) + " with filter '" + filter + "'"); final LocalChangeEventHandler localHandler = new LocalChangeEventHandler(listener, filter); if (LOG.isDebugEnabled()) LOG.debug("localHandlers size before unregister: " + this.localHandlers.size()); if (!this.localHandlers.remove(localHandler)) throw new CtxEventMgrException( "Could not unregister local context change event listener from topics " + Arrays.toString(topics) + " with filter '" + filter + "': Listener was not registered"); if (LOG.isDebugEnabled()) LOG.debug("localHandlers size after unregister: " + this.localHandlers.size()); this.eventMgr.unSubscribeInternalEvent(localHandler, topics, filter); } private void registerRemoteChangeListener(final IIdentity pubsubId, final CtxChangeEventListener listener, final String[] topics, final String filter) throws CtxException { if (LOG.isInfoEnabled()) LOG.info("Registering remote context change event listener to pubsubId " + pubsubId + " and topics " + Arrays.toString(topics) + " with filter '" + filter + "'"); try { for (int i = 0; i < topics.length; ++i) this.pubsubClient.subscriberSubscribe(pubsubId, topics[i], new RemoteChangeEventHandler(listener, filter)); } catch (Exception e) { throw new CtxEventMgrException( "Could not register remote context change event listener to pubsubId " + pubsubId + " and topics " + Arrays.toString(topics) + " with filter '" + filter + "': " + e.getLocalizedMessage(), e); } } private class LocalChangeEventHandler extends EventListener { /** The listener to forward CtxChangeEvents. */ private final CtxChangeEventListener listener; /** * The filter used for the EventListener registration. * * @see #hashCode() * @see #equals(Object) */ private final String filter; private LocalChangeEventHandler(final CtxChangeEventListener listener, final String filter) { this.listener = listener; this.filter = filter; } /* * @see org.societies.api.osgi.event.EventListener#handleExternalEvent(org.societies.api.osgi.event.CSSEvent) */ @Override public void handleExternalEvent(CSSEvent cssEvent) { LOG.warn("Received unexpected external CSS event" + ": type=" + cssEvent.geteventType() + ", name=" + cssEvent.geteventName() + ", source=" + cssEvent.geteventSource()); } /* * @see org.societies.api.osgi.event.EventListener#handleInternalEvent(org.societies.api.osgi.event.InternalEvent) */ @Override public void handleInternalEvent(InternalEvent internalEvent) { try { this.checkEventProps(internalEvent); final CtxChangeEvent ctxChangeEvent = new CtxChangeEvent( (CtxIdentifier) internalEvent.geteventInfo()); final String topic = internalEvent.geteventType(); if (CtxChangeEventTopic.CREATED.equals(topic)) this.listener.onCreation(ctxChangeEvent); else if (CtxChangeEventTopic.UPDATED.equals(topic)) this.listener.onUpdate(ctxChangeEvent); else if (CtxChangeEventTopic.MODIFIED.equals(topic)) this.listener.onModification(ctxChangeEvent); else if (CtxChangeEventTopic.REMOVED.equals(topic)) this.listener.onRemoval(ctxChangeEvent); else LOG.error("Unexpected local context change event topic: '" + topic + "'"); } catch (CtxEventMgrException ceme) { LOG.error("Malformed local context change event: " + ceme.getLocalizedMessage(), ceme); } } private void checkEventProps(final InternalEvent internalEvent) throws CtxEventMgrException { if (!(internalEvent.geteventInfo() instanceof CtxIdentifier)) throw new CtxEventMgrException("internal event info is missing or incorrect"); } /* * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((this.listener == null) ? 0 : this.listener.hashCode()); result = prime * result + ((this.filter == null) ? 0 : this.filter.hashCode()); return result; } /* * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object that) { if (this == that) return true; if (that == null) return false; if (this.getClass() != that.getClass()) return false; LocalChangeEventHandler other = (LocalChangeEventHandler) that; if (this.listener == null) { if (other.listener != null) return false; } else if (!this.listener.equals(other.listener)) return false; if (this.filter == null) { if (other.filter != null) return false; } else if (!this.filter.equals(other.filter)) return false; return true; } } private class RemoteChangeEventHandler implements Subscriber { /** The listener to forward CtxChangeEvents. */ private final CtxChangeEventListener listener; /** The regular expression to match context identifiers. */ private final Pattern patternFilter; private RemoteChangeEventHandler(final CtxChangeEventListener listener, final String filter) { this.listener = listener; this.patternFilter = Pattern.compile(filter); } /* * @see org.societies.api.comm.xmpp.pubsub.Subscriber#pubsubEvent(org.societies.api.identity.IIdentity, java.lang.String, java.lang.String, java.lang.Object) */ @Override public void pubsubEvent(IIdentity pubsubService, String node, String itemId, Object payload) { if (LOG.isDebugEnabled()) LOG.debug("pubsubEvent:pubsubService=" + pubsubService + ",node=" + node + ",itemId=" + itemId + ",payload=" + payload); try { if (itemId == null) { LOG.error("Remote context change event itemId can't be null"); return; } // Check if itemId, i.e. ctxId, matches against filter final Matcher filterMatcher = this.patternFilter.matcher(itemId); if (!filterMatcher.matches()) { if (LOG.isDebugEnabled()) LOG.debug("Ignoring remote context change event for ctxId " + itemId); return; } final CtxIdentifier ctxId = CtxIdentifierFactory.getInstance().fromString(itemId); final CtxChangeEvent ctxChangeEvent = new CtxChangeEvent(ctxId); final String topic = node; if (CtxChangeEventTopic.CREATED.equals(topic)) this.listener.onCreation(ctxChangeEvent); else if (CtxChangeEventTopic.UPDATED.equals(topic)) this.listener.onUpdate(ctxChangeEvent); else if (CtxChangeEventTopic.MODIFIED.equals(topic)) this.listener.onModification(ctxChangeEvent); else if (CtxChangeEventTopic.REMOVED.equals(topic)) this.listener.onRemoval(ctxChangeEvent); else LOG.error("Unexpected remote context change event topic: '" + topic + "'"); } catch (MalformedCtxIdentifierException mcie) { LOG.error("Malformed context identifier in remote context change event: " + mcie.getLocalizedMessage(), mcie); } } } }