| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| package org.apache.openjpa.event; |
| |
| import java.io.Serializable; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.meta.ClassMetaData; |
| import org.apache.openjpa.meta.MetaDataDefaults; |
| import org.apache.openjpa.util.InvalidStateException; |
| |
| /** |
| * Manager that can be used to track and notify listeners on lifecycle events. |
| * This class is optimized for event firing rather than for adding and |
| * removing listeners, which are O(n) operations. This class also does not |
| * maintain perfect set semantics for listeners; it is possible to wind up |
| * having the same listener invoked multiple times for a single event if it |
| * is added to this manager multiple times with different classes, or with |
| * a base class and its subclass. |
| * |
| * @author Steve Kim |
| * @author Abe White |
| * @since 0.3.3 |
| */ |
| public class LifecycleEventManager |
| implements CallbackModes, Serializable { |
| |
| private static final long serialVersionUID = 1L; |
| |
| private static final Exception[] EMPTY_EXCEPTIONS = new Exception[0]; |
| |
| private static final Localizer _loc = Localizer.forPackage( |
| LifecycleEventManager.class); |
| |
| private Map<Class<?>, ListenerList> _classListeners = null; |
| private ListenerList _listeners = null; |
| // odd-element: Listener even-element: Class[] |
| private List<Object> _addListeners = new LinkedList<>(); |
| private List<Object> _remListeners = new LinkedList<>(); |
| private List<Exception> _exceps = new LinkedList<>(); |
| private boolean _firing = false; |
| private boolean _fail = false; |
| private boolean _failFast = false; |
| private boolean _activated = false; // set to true once modified |
| |
| /** |
| * Whether this LifeCycleEventManager has had at least one listener or callback |
| * registered. Used for a quick test when firing events. |
| * @return boolean |
| */ |
| public boolean isActive(ClassMetaData meta) { |
| return _activated || |
| meta.getLifecycleMetaData().is_activated() || |
| meta.getRepository().is_systemListenersActivated(); |
| } |
| |
| /** |
| * Whether to fail after first exception when firing events to listeners. |
| */ |
| public boolean isFailFast() { |
| return _failFast; |
| } |
| |
| /** |
| * Whether to fail after first exception when firing events to listeners. |
| */ |
| public void setFailFast(boolean failFast) { |
| _failFast = failFast; |
| } |
| |
| /** |
| * Register a lifecycle listener for the given classes. If the classes |
| * array is null, register for all classes. |
| */ |
| public synchronized void addListener(Object listener, Class<?>[] classes) { |
| if (listener == null) |
| return; |
| if (classes != null && classes.length == 0) |
| return; |
| _activated = true; |
| if (_firing) { |
| _addListeners.add(listener); |
| _addListeners.add(classes); |
| return; |
| } |
| |
| if (classes == null) { |
| if (_listeners == null) |
| _listeners = new ListenerList(5); |
| _listeners.add(listener); |
| return; |
| } |
| |
| if (_classListeners == null) |
| _classListeners = new HashMap<>(); |
| ListenerList listeners; |
| for (Class<?> aClass : classes) { |
| listeners = _classListeners.get(aClass); |
| if (listeners == null) { |
| listeners = new ListenerList(3); |
| _classListeners.put(aClass, listeners); |
| } |
| listeners.add(listener); |
| } |
| } |
| |
| /** |
| * Remove the given listener. |
| */ |
| public synchronized void removeListener(Object listener) { |
| if (_firing) { |
| _remListeners.add(listener); |
| return; |
| } |
| |
| if (_listeners != null && _listeners.remove(listener)) |
| return; |
| if (_classListeners != null) { |
| ListenerList listeners; |
| for (ListenerList objects : _classListeners.values()) { |
| listeners = objects; |
| listeners.remove(listener); |
| } |
| } |
| } |
| |
| /** |
| * Return whether there are listeners or callbacks for the given source. |
| */ |
| public boolean hasPersistListeners(Object source, ClassMetaData meta) { |
| return hasHandlers(source, meta, LifecycleEvent.BEFORE_PERSIST) |
| || hasHandlers(source, meta, LifecycleEvent.AFTER_PERSIST) |
| || hasHandlers(source, meta, |
| LifecycleEvent.AFTER_PERSIST_PERFORMED); |
| } |
| |
| /** |
| * Return whether there are listeners or callbacks for the given source. |
| */ |
| public boolean hasDeleteListeners(Object source, ClassMetaData meta) { |
| return hasHandlers(source, meta, LifecycleEvent.BEFORE_DELETE) |
| || hasHandlers(source, meta, LifecycleEvent.AFTER_DELETE) |
| || hasHandlers(source, meta, LifecycleEvent.AFTER_DELETE_PERFORMED); |
| } |
| |
| /** |
| * Return whether there are listeners or callbacks for the given source. |
| */ |
| public boolean hasClearListeners(Object source, ClassMetaData meta) { |
| return hasHandlers(source, meta, LifecycleEvent.BEFORE_CLEAR) |
| || hasHandlers(source, meta, LifecycleEvent.AFTER_CLEAR); |
| } |
| |
| /** |
| * Return whether there are listeners or callbacks for the given source. |
| */ |
| public boolean hasLoadListeners(Object source, ClassMetaData meta) { |
| return hasHandlers(source, meta, LifecycleEvent.AFTER_LOAD); |
| } |
| |
| /** |
| * Return whether there are listeners or callbacks for the given source. |
| */ |
| public boolean hasStoreListeners(Object source, ClassMetaData meta) { |
| return hasHandlers(source, meta, LifecycleEvent.BEFORE_STORE) |
| || hasHandlers(source, meta, LifecycleEvent.AFTER_STORE); |
| } |
| |
| /** |
| * Return whether there are listeners or callbacks for the given source. |
| */ |
| public boolean hasUpdateListeners(Object source, ClassMetaData meta) { |
| return hasHandlers(source, meta, LifecycleEvent.BEFORE_UPDATE) |
| || hasHandlers(source, meta, LifecycleEvent.AFTER_UPDATE_PERFORMED); |
| } |
| |
| /** |
| * Return whether there are listeners or callbacks for the given source. |
| */ |
| public boolean hasDirtyListeners(Object source, ClassMetaData meta) { |
| return hasHandlers(source, meta, LifecycleEvent.BEFORE_DIRTY) |
| || hasHandlers(source, meta, LifecycleEvent.AFTER_DIRTY); |
| } |
| |
| /** |
| * Return whether there are listeners or callbacks for the given source. |
| */ |
| public boolean hasDetachListeners(Object source, ClassMetaData meta) { |
| return hasHandlers(source, meta, LifecycleEvent.BEFORE_DETACH) |
| || hasHandlers(source, meta, LifecycleEvent.AFTER_DETACH); |
| } |
| |
| /** |
| * Return whether there are listeners or callbacks for the given source. |
| */ |
| public boolean hasAttachListeners(Object source, ClassMetaData meta) { |
| return hasHandlers(source, meta, LifecycleEvent.BEFORE_ATTACH) |
| || hasHandlers(source, meta, LifecycleEvent.AFTER_ATTACH); |
| } |
| |
| private boolean hasHandlers(Object source, ClassMetaData meta, int type) { |
| return hasCallbacks(source, meta, type) |
| || hasListeners(source, meta, type); |
| } |
| |
| /** |
| * Return true if any callbacks are registered for the given source and |
| * event type. |
| */ |
| private boolean hasCallbacks(Object source, ClassMetaData meta, int type) { |
| LifecycleCallbacks[] callbacks = meta.getLifecycleMetaData(). |
| getCallbacks(type); |
| if (callbacks.length == 0) |
| return false; |
| for (LifecycleCallbacks callback : callbacks) |
| if (callback.hasCallback(source, type)) |
| return true; |
| return false; |
| } |
| |
| /** |
| * Return true if any listeners are registered for the given source and |
| * event type. |
| */ |
| private boolean hasListeners(Object source, |
| ClassMetaData meta, int type) { |
| if (meta.getLifecycleMetaData().getIgnoreSystemListeners()) |
| return false; |
| if (fireEvent(null, source, null, type, _listeners, true, null) == Boolean.TRUE) |
| return true; |
| ListenerList system = meta.getRepository().getSystemListeners(); |
| if (!system.isEmpty() && fireEvent(null, source, null, type, system, |
| true, null) == Boolean.TRUE) |
| return true; |
| if (_classListeners != null) { |
| Class<?> c = source == null ? meta.getDescribedType() : source.getClass(); |
| do { |
| if (fireEvent(null, source, null, type, _classListeners.get(c), true, null) == Boolean.TRUE) |
| return true; |
| c = c.getSuperclass(); |
| } while (c != null && c != Object.class); |
| } |
| return false; |
| } |
| |
| /** |
| * Fire lifecycle event to all registered listeners without an argument. |
| */ |
| public Exception[] fireEvent(Object source, |
| ClassMetaData meta, int type) { |
| return fireEvent(source, null, meta, type); |
| } |
| |
| /** |
| * Fire lifecycle event to all registered listeners. |
| */ |
| public synchronized Exception[] fireEvent(Object source, Object related, |
| ClassMetaData meta, int type) { |
| boolean reentrant = _firing; |
| _firing = true; |
| List<Exception> exceptions = (reentrant) ? new LinkedList<>() : _exceps; |
| MetaDataDefaults def = meta.getRepository().getMetaDataFactory(). |
| getDefaults(); |
| |
| boolean callbacks = def.getCallbacksBeforeListeners(type); |
| if (callbacks) |
| makeCallbacks(source, related, meta, type, exceptions); |
| |
| LifecycleEvent ev = (LifecycleEvent) fireEvent(null, source, related, |
| type, _listeners, false, exceptions); |
| |
| if (_classListeners != null) { |
| Class<?> c = source == null ? meta.getDescribedType() : source.getClass(); |
| do { |
| ev = (LifecycleEvent) fireEvent(ev, source, related, type, |
| _classListeners.get(c), false, exceptions); |
| c = c.getSuperclass(); |
| } while (c != null && c != Object.class); |
| } |
| |
| // make system listeners |
| if (!meta.getLifecycleMetaData().getIgnoreSystemListeners()) { |
| ListenerList system = meta.getRepository().getSystemListeners(); |
| fireEvent(ev, source, related, type, system, false, exceptions); |
| } |
| |
| if (!callbacks) |
| makeCallbacks(source, related, meta, type, exceptions); |
| |
| // create return array before clearing exceptions |
| Exception[] ret; |
| if (exceptions.isEmpty()) |
| ret = EMPTY_EXCEPTIONS; |
| else |
| ret = exceptions.toArray |
| (new Exception[exceptions.size()]); |
| |
| // if this wasn't a reentrant call, catch up with calls to add |
| // and remove listeners made while firing |
| if (!reentrant) { |
| _firing = false; |
| _fail = false; |
| if (!_addListeners.isEmpty()) |
| for (Iterator<Object> itr = _addListeners.iterator(); itr.hasNext();) { |
| addListener(itr.next(), (Class[]) itr.next()); |
| } |
| if (!_remListeners.isEmpty()) |
| for (Object remListener : _remListeners) { |
| removeListener(remListener); |
| } |
| _addListeners.clear(); |
| _remListeners.clear(); |
| _exceps.clear(); |
| } |
| return ret; |
| } |
| |
| /** |
| * Make callbacks, recording any exceptions in the given collection. |
| */ |
| private void makeCallbacks(Object source, Object related, |
| ClassMetaData meta, int type, Collection<Exception> exceptions) { |
| // make lifecycle callbacks |
| LifecycleCallbacks[] callbacks = meta.getLifecycleMetaData(). |
| getCallbacks(type); |
| for (int i = 0; !_fail && i < callbacks.length; i++) { |
| try { |
| callbacks[i].makeCallback(source, related, type); |
| } catch (Exception e) { |
| exceptions.add(e); |
| if (_failFast) |
| _fail = true; |
| } |
| } |
| } |
| |
| /** |
| * Fire an event with the given source and type to the given list of |
| * listeners. The event may have already been constructed. |
| */ |
| private Object fireEvent(LifecycleEvent ev, Object source, Object rel, |
| int type, ListenerList listeners, boolean mock, List<Exception> exceptions) { |
| if (listeners == null || !listeners.hasListeners(type)) |
| return null; |
| |
| Object listener; |
| boolean responds; |
| for (int i = 0, size = listeners.size(); !_fail && i < size; i++) { |
| listener = listeners.get(i); |
| if (size == 1) |
| responds = true; |
| else if (listener instanceof ListenerAdapter) { |
| responds = ((ListenerAdapter) listener).respondsTo(type); |
| if (!responds) |
| continue; |
| } else { |
| responds = false; |
| } |
| try { |
| switch (type) { |
| case LifecycleEvent.BEFORE_CLEAR: |
| case LifecycleEvent.AFTER_CLEAR: |
| if (responds || listener instanceof ClearListener) { |
| if (mock) |
| return Boolean.TRUE; |
| if (ev == null) |
| ev = new LifecycleEvent(source, type); |
| if (type == LifecycleEvent.BEFORE_CLEAR) |
| ((ClearListener) listener).beforeClear(ev); |
| else |
| ((ClearListener) listener).afterClear(ev); |
| } |
| break; |
| case LifecycleEvent.BEFORE_PERSIST: |
| case LifecycleEvent.AFTER_PERSIST: |
| if (responds || listener instanceof PersistListener) { |
| if (mock) |
| return Boolean.TRUE; |
| if (ev == null) |
| ev = new LifecycleEvent(source, type); |
| if (type == LifecycleEvent.BEFORE_PERSIST) |
| ((PersistListener) listener).beforePersist(ev); |
| else |
| ((PersistListener) listener).afterPersist(ev); |
| } |
| break; |
| case LifecycleEvent.BEFORE_DELETE: |
| case LifecycleEvent.AFTER_DELETE: |
| if (responds || listener instanceof DeleteListener) { |
| if (mock) |
| return Boolean.TRUE; |
| if (ev == null) |
| ev = new LifecycleEvent(source, type); |
| if (type == LifecycleEvent.BEFORE_DELETE) |
| ((DeleteListener) listener).beforeDelete(ev); |
| else |
| ((DeleteListener) listener).afterDelete(ev); |
| } |
| break; |
| case LifecycleEvent.BEFORE_DIRTY: |
| case LifecycleEvent.AFTER_DIRTY: |
| case LifecycleEvent.BEFORE_DIRTY_FLUSHED: |
| case LifecycleEvent.AFTER_DIRTY_FLUSHED: |
| if (responds || listener instanceof DirtyListener) { |
| if (mock) |
| return Boolean.TRUE; |
| if (ev == null) |
| ev = new LifecycleEvent(source, type); |
| switch (type) { |
| case LifecycleEvent.BEFORE_DIRTY: |
| ((DirtyListener) listener).beforeDirty(ev); |
| break; |
| case LifecycleEvent.AFTER_DIRTY: |
| ((DirtyListener) listener).afterDirty(ev); |
| break; |
| case LifecycleEvent.BEFORE_DIRTY_FLUSHED: |
| ((DirtyListener) listener) |
| .beforeDirtyFlushed(ev); |
| break; |
| case LifecycleEvent.AFTER_DIRTY_FLUSHED: |
| ((DirtyListener) listener) |
| .afterDirtyFlushed(ev); |
| break; |
| } |
| } |
| break; |
| case LifecycleEvent.AFTER_LOAD: |
| case LifecycleEvent.AFTER_REFRESH: |
| if (responds || listener instanceof LoadListener) { |
| if (mock) |
| return Boolean.TRUE; |
| if (ev == null) |
| ev = new LifecycleEvent(source, type); |
| if (type == LifecycleEvent.AFTER_LOAD) |
| ((LoadListener) listener).afterLoad(ev); |
| else |
| ((LoadListener) listener).afterRefresh(ev); |
| } |
| break; |
| case LifecycleEvent.BEFORE_STORE: |
| case LifecycleEvent.AFTER_STORE: |
| if (responds || listener instanceof StoreListener) { |
| if (mock) |
| return Boolean.TRUE; |
| if (ev == null) |
| ev = new LifecycleEvent(source, type); |
| if (type == LifecycleEvent.BEFORE_STORE) |
| ((StoreListener) listener).beforeStore(ev); |
| else |
| ((StoreListener) listener).afterStore(ev); |
| } |
| break; |
| case LifecycleEvent.BEFORE_DETACH: |
| case LifecycleEvent.AFTER_DETACH: |
| if (responds || listener instanceof DetachListener) { |
| if (mock) |
| return Boolean.TRUE; |
| if (ev == null) |
| ev = new LifecycleEvent(source, rel, type); |
| if (type == LifecycleEvent.BEFORE_DETACH) |
| ((DetachListener) listener).beforeDetach(ev); |
| else |
| ((DetachListener) listener).afterDetach(ev); |
| } |
| break; |
| case LifecycleEvent.BEFORE_ATTACH: |
| case LifecycleEvent.AFTER_ATTACH: |
| if (responds || listener instanceof AttachListener) { |
| if (mock) |
| return Boolean.TRUE; |
| if (ev == null) |
| ev = new LifecycleEvent(source, rel, type); |
| if (type == LifecycleEvent.BEFORE_ATTACH) |
| ((AttachListener) listener).beforeAttach(ev); |
| else |
| ((AttachListener) listener).afterAttach(ev); |
| } |
| break; |
| |
| case LifecycleEvent.AFTER_PERSIST_PERFORMED: |
| if (responds || listener instanceof PostPersistListener) { |
| if (mock) |
| return Boolean.TRUE; |
| if (ev == null) |
| ev = new LifecycleEvent(source, rel, type); |
| ((PostPersistListener) listener).afterPersistPerformed(ev); |
| } |
| break; |
| case LifecycleEvent.BEFORE_UPDATE: |
| case LifecycleEvent.AFTER_UPDATE_PERFORMED: |
| if (responds || listener instanceof UpdateListener) { |
| if (mock) |
| return Boolean.TRUE; |
| if (ev == null) |
| ev = new LifecycleEvent(source, rel, type); |
| if (type == LifecycleEvent.BEFORE_UPDATE) |
| ((UpdateListener) listener).beforeUpdate(ev); |
| else |
| ((UpdateListener) listener).afterUpdatePerformed(ev); |
| } |
| break; |
| case LifecycleEvent.AFTER_DELETE_PERFORMED: |
| if (responds || listener instanceof PostDeleteListener){ |
| if (mock) |
| return Boolean.TRUE; |
| if (ev == null) |
| ev = new LifecycleEvent(source, rel, type); |
| ((PostDeleteListener) listener).afterDeletePerformed(ev); |
| } |
| break; |
| default: |
| throw new InvalidStateException(_loc.get("unknown-lifecycle-event", Integer.toString(type))); |
| } |
| } |
| catch (Exception e) { |
| exceptions.add(e); |
| if (_failFast) |
| _fail = true; |
| } |
| } |
| return ev; |
| } |
| |
| /** |
| * Interface that facades to other lifecycle listener interfaces can |
| * implement to choose which events to respond to based on their delegate. |
| * This is more efficient than registering as a listener for all events |
| * but only responding to some. |
| */ |
| public interface ListenerAdapter { |
| |
| /** |
| * Return whether this instance responds to the given event type from |
| * {@link LifecycleEvent}. |
| */ |
| boolean respondsTo(int eventType); |
| } |
| |
| /** |
| * Extended list that tracks what event types its elements care about. |
| * Maintains set semantics as well. |
| */ |
| public static class ListenerList extends ArrayList<Object> { |
| |
| |
| private static final long serialVersionUID = 1L; |
| private int _types = 0; |
| |
| public ListenerList(int size) { |
| super(size); |
| } |
| |
| public ListenerList(ListenerList copy) { |
| super(copy); |
| _types = copy._types; |
| } |
| |
| public boolean hasListeners(int type) { |
| return (_types & (2 << type)) > 0; |
| } |
| |
| @Override |
| public boolean add(Object listener) { |
| if (contains(listener)) |
| return false; |
| super.add(listener); |
| _types |= getEventTypes(listener); |
| return true; |
| } |
| |
| @Override |
| public boolean remove(Object listener) { |
| if (!super.remove(listener)) |
| return false; |
| |
| // recompute types mask |
| _types = 0; |
| for (int i = 0; i < size(); i++) |
| _types |= getEventTypes(get(i)); |
| return true; |
| } |
| |
| /** |
| * Return a mask of the event types the given listener processes. |
| */ |
| private static int getEventTypes(Object listener) { |
| int types = 0; |
| if (listener instanceof ListenerAdapter) { |
| ListenerAdapter adapter = (ListenerAdapter) listener; |
| for (int i = 0; i < LifecycleEvent.ALL_EVENTS.length; i++) |
| if (adapter.respondsTo(LifecycleEvent.ALL_EVENTS[i])) |
| types |= 2 << LifecycleEvent.ALL_EVENTS[i]; |
| return types; |
| } |
| |
| if (listener instanceof PersistListener) { |
| types |= 2 << LifecycleEvent.BEFORE_PERSIST; |
| types |= 2 << LifecycleEvent.AFTER_PERSIST; |
| } |
| if (listener instanceof ClearListener) { |
| types |= 2 << LifecycleEvent.BEFORE_CLEAR; |
| types |= 2 << LifecycleEvent.AFTER_CLEAR; |
| } |
| if (listener instanceof DeleteListener) { |
| types |= 2 << LifecycleEvent.BEFORE_DELETE; |
| types |= 2 << LifecycleEvent.AFTER_DELETE; |
| } |
| if (listener instanceof DirtyListener) { |
| types |= 2 << LifecycleEvent.BEFORE_DIRTY; |
| types |= 2 << LifecycleEvent.AFTER_DIRTY; |
| types |= 2 << LifecycleEvent.BEFORE_DIRTY_FLUSHED; |
| types |= 2 << LifecycleEvent.AFTER_DIRTY_FLUSHED; |
| } |
| if (listener instanceof LoadListener) { |
| types |= 2 << LifecycleEvent.AFTER_LOAD; |
| types |= 2 << LifecycleEvent.AFTER_REFRESH; |
| } |
| if (listener instanceof StoreListener) { |
| types |= 2 << LifecycleEvent.BEFORE_STORE; |
| types |= 2 << LifecycleEvent.AFTER_STORE; |
| } |
| if (listener instanceof DetachListener) { |
| types |= 2 << LifecycleEvent.BEFORE_DETACH; |
| types |= 2 << LifecycleEvent.AFTER_DETACH; |
| } |
| if (listener instanceof AttachListener) { |
| types |= 2 << LifecycleEvent.BEFORE_ATTACH; |
| types |= 2 << LifecycleEvent.AFTER_ATTACH; |
| } |
| return types; |
| } |
| } |
| } |