/* * (C) Copyright 2006-2013 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Bogdan Stefanescu * Florent Guillaume */ package org.nuxeo.ecm.core.event.impl; import java.io.IOException; import java.net.URL; import java.util.HashSet; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.common.xmap.annotation.XNode; import org.nuxeo.common.xmap.annotation.XNodeList; import org.nuxeo.common.xmap.annotation.XObject; import org.nuxeo.ecm.core.event.Event; import org.nuxeo.ecm.core.event.EventBundle; import org.nuxeo.ecm.core.event.EventListener; import org.nuxeo.ecm.core.event.PostCommitEventListener; import org.nuxeo.ecm.core.event.PostCommitFilteringEventListener; import org.nuxeo.ecm.core.event.script.Script; import org.nuxeo.ecm.core.event.script.ScriptingEventListener; import org.nuxeo.ecm.core.event.script.ScriptingPostCommitEventListener; import org.nuxeo.runtime.model.RuntimeContext; /** * XObject descriptor to declare event listeners */ @XObject("listener") public class EventListenerDescriptor { public static final Log log = LogFactory.getLog(EventListenerDescriptor.class); @XNode("@name") protected String name; /** * The event listener class. */ @XNode("@class") protected String className; /** * A script reference: URL, file path, or bundle entry. Runtime variable are expanded. To specify a bundle entry use * the URL schema "bundle:" */ @XNode("@script") protected String script; /** * Applies only for scripts. */ @XNode("@postCommit") protected boolean isPostCommit; /** * Applies only for post commit listener */ @XNode("@async") protected Boolean isAsync; @XNode("@transactionTimeOut") protected Integer transactionTimeOut; /** * The priority to be used to order listeners. */ @XNode("@priority") protected Integer priority; @XNode("@enabled") protected boolean isEnabled = true; @XNode("@retryCount") protected Integer retryCount; @XNode("@singlethread") protected boolean singleThreaded = false; protected Set<String> events; protected RuntimeContext rc; protected EventListener inLineListener; protected PostCommitEventListener postCommitEventListener; public int getPriority() { return priority == null ? 0 : priority.intValue(); } public void setRuntimeContext(RuntimeContext rc) { this.rc = rc; } public RuntimeContext getRuntimeContext() { return rc; } public boolean isEnabled() { return isEnabled; } public Integer getRetryCount() { return retryCount; } public Set<String> getEvents() { return events; } @XNodeList(value = "event", componentType = String.class, type = HashSet.class, nullByDefault = true) public void setEvents(Set<String> events) { this.events = events.isEmpty() ? null : events; } public void setEnabled(boolean isEnabled) { this.isEnabled = isEnabled; } public void setRetryCount(Integer retryCount) { this.retryCount = retryCount; } public void initListener() { try { if (className != null) { Class<?> klass; try { klass = getRuntimeContext().loadClass(className); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } if (EventListener.class.isAssignableFrom(klass)) { inLineListener = (EventListener) klass.newInstance(); isPostCommit = false; } else if (PostCommitEventListener.class.isAssignableFrom(klass)) { postCommitEventListener = (PostCommitEventListener) klass.newInstance(); isPostCommit = true; } else { throw new IllegalArgumentException( "Listener extension must define a class extending EventListener or PostCommitEventListener: '" + className + "'."); } } else if (script != null) { if (isPostCommit) { postCommitEventListener = new ScriptingPostCommitEventListener(getScript()); } else { inLineListener = new ScriptingEventListener(getScript()); } } else { throw new IllegalArgumentException("Listener extension must define either a class or a script"); } } catch (ReflectiveOperationException | NoClassDefFoundError | IOException e) { throw new RuntimeException(e); } } public EventListener asEventListener() { return inLineListener; } public PostCommitEventListener asPostCommitListener() { return postCommitEventListener; } public Script getScript() throws IOException { if (rc != null) { URL url = rc.getBundle().getEntry(script); if (url == null) { // if not found using bundle entries try using classloader // in a test environment bundle entries may not work url = rc.getResource(script); if (url == null) { throw new IOException("Script Not found: " + script); } } return Script.newScript(url); } else { return Script.newScript(script); } } public String getName() { if (name == null) { if (className != null) { name = className; } else { name = script; } } return name; } public Integer getTransactionTimeout() { return transactionTimeOut; } public void merge(EventListenerDescriptor other) { isEnabled = other.isEnabled; if (other.className != null) { className = other.className; rc = other.rc; } else if (other.script != null) { script = other.script; className = null; rc = other.rc; } if (other.isAsync != null) { isAsync = other.isAsync; } if (other.events != null) { events = other.events; } if (other.transactionTimeOut != null) { transactionTimeOut = other.transactionTimeOut; } if (other.priority != null) { priority = other.priority; } if (other.retryCount != null) { retryCount = other.retryCount; } } public final boolean acceptEvent(String eventName) { return events == null || events.contains(eventName); } public void setIsAsync(Boolean isAsync) { this.isAsync = isAsync; } public boolean getIsAsync() { return isAsync == null ? false : isAsync.booleanValue(); } public boolean isSingleThreaded() { return singleThreaded; } /** * Filters the event bundle to only keep events of interest to this listener. * * @since 5.7 */ public EventBundle filterBundle(EventBundle bundle) { EventBundle filtered = new EventBundleImpl(); for (Event event : bundle) { if (!acceptEvent(event.getName())) { continue; } PostCommitEventListener pcl = asPostCommitListener(); if (pcl instanceof PostCommitFilteringEventListener && !((PostCommitFilteringEventListener) pcl).acceptEvent(event)) { continue; } filtered.push(event); } return filtered; } /** * Checks if there's at least one event of interest in the bundle. * * @since 5.7 */ public boolean acceptBundle(EventBundle bundle) { for (Event event : bundle) { if (!acceptEvent(event.getName())) { continue; } PostCommitEventListener pcl = asPostCommitListener(); if (pcl instanceof PostCommitFilteringEventListener && !((PostCommitFilteringEventListener) pcl).acceptEvent(event)) { continue; } return true; } return false; } }