001/* 002 * Copyright (c) 2006-2013 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Bogdan Stefanescu 011 * Florent Guillaume 012 */ 013package org.nuxeo.ecm.core.event.impl; 014 015import java.io.IOException; 016import java.net.URL; 017import java.util.HashSet; 018import java.util.Set; 019 020import org.apache.commons.logging.Log; 021import org.apache.commons.logging.LogFactory; 022import org.nuxeo.common.xmap.annotation.XNode; 023import org.nuxeo.common.xmap.annotation.XNodeList; 024import org.nuxeo.common.xmap.annotation.XObject; 025import org.nuxeo.ecm.core.event.Event; 026import org.nuxeo.ecm.core.event.EventBundle; 027import org.nuxeo.ecm.core.event.EventListener; 028import org.nuxeo.ecm.core.event.PostCommitEventListener; 029import org.nuxeo.ecm.core.event.PostCommitFilteringEventListener; 030import org.nuxeo.ecm.core.event.script.Script; 031import org.nuxeo.ecm.core.event.script.ScriptingEventListener; 032import org.nuxeo.ecm.core.event.script.ScriptingPostCommitEventListener; 033import org.nuxeo.runtime.model.RuntimeContext; 034 035/** 036 * XObject descriptor to declare event listeners 037 */ 038@XObject("listener") 039public class EventListenerDescriptor { 040 041 public static final Log log = LogFactory.getLog(EventListenerDescriptor.class); 042 043 @XNode("@name") 044 protected String name; 045 046 /** 047 * The event listener class. 048 */ 049 @XNode("@class") 050 protected Class<?> clazz; 051 052 /** 053 * A script reference: URL, file path, or bundle entry. Runtime variable are expanded. To specify a bundle entry use 054 * the URL schema "bundle:" 055 */ 056 @XNode("@script") 057 protected String script; 058 059 /** 060 * Applies only for scripts. 061 */ 062 @XNode("@postCommit") 063 protected boolean isPostCommit; 064 065 /** 066 * Applies only for post commit listener 067 */ 068 @XNode("@async") 069 protected Boolean isAsync; 070 071 @XNode("@transactionTimeOut") 072 protected Integer transactionTimeOut; 073 074 /** 075 * The priority to be used to order listeners. 076 */ 077 @XNode("@priority") 078 protected Integer priority; 079 080 @XNode("@enabled") 081 protected boolean isEnabled = true; 082 083 @XNode("@retryCount") 084 protected Integer retryCount; 085 086 @XNode("@singlethread") 087 protected boolean singleThreaded = false; 088 089 protected Set<String> events; 090 091 protected RuntimeContext rc; 092 093 protected EventListener inLineListener; 094 095 protected PostCommitEventListener postCommitEventListener; 096 097 public int getPriority() { 098 return priority == null ? 0 : priority.intValue(); 099 } 100 101 public void setRuntimeContext(RuntimeContext rc) { 102 this.rc = rc; 103 } 104 105 public RuntimeContext getRuntimeContext() { 106 return rc; 107 } 108 109 public boolean isEnabled() { 110 return isEnabled; 111 } 112 113 public Integer getRetryCount() { 114 return retryCount; 115 } 116 117 public Set<String> getEvents() { 118 return events; 119 } 120 121 @XNodeList(value = "event", componentType = String.class, type = HashSet.class, nullByDefault = true) 122 public void setEvents(Set<String> events) { 123 this.events = events.isEmpty() ? null : events; 124 } 125 126 public void setEnabled(boolean isEnabled) { 127 this.isEnabled = isEnabled; 128 } 129 130 public void setRetryCount(Integer retryCount) { 131 this.retryCount = retryCount; 132 } 133 134 public void initListener() { 135 try { 136 if (clazz != null) { 137 if (EventListener.class.isAssignableFrom(clazz)) { 138 inLineListener = (EventListener) clazz.newInstance(); 139 isPostCommit = false; 140 } else if (PostCommitEventListener.class.isAssignableFrom(clazz)) { 141 postCommitEventListener = (PostCommitEventListener) clazz.newInstance(); 142 isPostCommit = true; 143 } 144 } else if (script != null) { 145 if (isPostCommit) { 146 postCommitEventListener = new ScriptingPostCommitEventListener(getScript()); 147 } else { 148 inLineListener = new ScriptingEventListener(getScript()); 149 } 150 } else { 151 throw new IllegalArgumentException("Listener extension must define either a class or a script"); 152 } 153 } catch (ReflectiveOperationException | IOException e) { 154 throw new RuntimeException(e); 155 } 156 } 157 158 public EventListener asEventListener() { 159 return inLineListener; 160 } 161 162 public PostCommitEventListener asPostCommitListener() { 163 return postCommitEventListener; 164 } 165 166 public Script getScript() throws IOException { 167 if (rc != null) { 168 URL url = rc.getBundle().getEntry(script); 169 if (url == null) { 170 // if not found using bundle entries try using classloader 171 // in a test environment bundle entries may not work 172 url = rc.getResource(script); 173 if (url == null) { 174 throw new IOException("Script Not found: " + script); 175 } 176 } 177 return Script.newScript(url); 178 } else { 179 return Script.newScript(script); 180 } 181 } 182 183 public String getName() { 184 if (name == null) { 185 if (clazz != null) { 186 name = clazz.getSimpleName(); 187 } else { 188 name = script; 189 } 190 } 191 return name; 192 } 193 194 public Integer getTransactionTimeout() { 195 return transactionTimeOut; 196 } 197 198 public void merge(EventListenerDescriptor other) { 199 200 this.isEnabled = other.isEnabled; 201 202 if (other.clazz != null) { 203 this.clazz = other.clazz; 204 this.rc = other.rc; 205 } else if (other.script != null) { 206 this.script = other.script; 207 this.clazz = null; 208 this.rc = other.rc; 209 } 210 211 if (other.isAsync != null) { 212 this.isAsync = other.isAsync; 213 } 214 215 if (other.events != null) { 216 this.events = other.events; 217 } 218 219 if (other.transactionTimeOut != null) { 220 this.transactionTimeOut = other.transactionTimeOut; 221 } 222 223 if (other.priority != null) { 224 this.priority = other.priority; 225 } 226 227 if (other.retryCount != null) { 228 this.retryCount = other.retryCount; 229 } 230 } 231 232 public final boolean acceptEvent(String eventName) { 233 return events == null || events.contains(eventName); 234 } 235 236 public void setIsAsync(Boolean isAsync) { 237 this.isAsync = isAsync; 238 } 239 240 public boolean getIsAsync() { 241 return isAsync == null ? false : isAsync.booleanValue(); 242 } 243 244 public boolean isSingleThreaded() { 245 return singleThreaded; 246 } 247 248 /** 249 * Filters the event bundle to only keep events of interest to this listener. 250 * 251 * @since 5.7 252 */ 253 public EventBundle filterBundle(EventBundle bundle) { 254 EventBundle filtered = new EventBundleImpl(); 255 for (Event event : bundle) { 256 if (!acceptEvent(event.getName())) { 257 continue; 258 } 259 PostCommitEventListener pcl = asPostCommitListener(); 260 if (pcl instanceof PostCommitFilteringEventListener 261 && !((PostCommitFilteringEventListener) pcl).acceptEvent(event)) { 262 continue; 263 } 264 filtered.push(event); 265 } 266 return filtered; 267 } 268 269 /** 270 * Checks if there's at least one event of interest in the bundle. 271 * 272 * @since 5.7 273 */ 274 public boolean acceptBundle(EventBundle bundle) { 275 for (Event event : bundle) { 276 if (!acceptEvent(event.getName())) { 277 continue; 278 } 279 PostCommitEventListener pcl = asPostCommitListener(); 280 if (pcl instanceof PostCommitFilteringEventListener 281 && !((PostCommitFilteringEventListener) pcl).acceptEvent(event)) { 282 continue; 283 } 284 return true; 285 } 286 return false; 287 } 288 289}