IDEMPIERE-5600 Add Core.getDefaultAnnotationBasedEventManager() API (#1698)
* IDEMPIERE-5600 Add Core.getDefaultAnnotationBasedEventManager() API * IDEMPIERE-5600 Add Core.getDefaultAnnotationBasedEventManager() API - minor refinement
This commit is contained in:
parent
7417e1ce3c
commit
859dd1a723
|
@ -1,4 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.adempiere.base.DefaultAnnotationBasedEventManager">
|
||||
<service>
|
||||
<provide interface="org.adempiere.base.DefaultAnnotationBasedEventManager"/>
|
||||
</service>
|
||||
<implementation class="org.adempiere.base.DefaultAnnotationBasedEventManager"/>
|
||||
</scr:component>
|
|
@ -27,6 +27,7 @@ package org.adempiere.base;
|
|||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
|
@ -73,9 +74,9 @@ public abstract class AnnotationBasedEventManager extends AnnotationBasedFactory
|
|||
|
||||
private static final CLogger s_log = CLogger.getCLogger(AnnotationBasedEventManager.class);
|
||||
|
||||
private IEventManager eventManager;
|
||||
private BundleContext bundleContext;
|
||||
private List<EventHandler> handlers = new ArrayList<>();
|
||||
protected IEventManager eventManager;
|
||||
protected BundleContext bundleContext;
|
||||
protected List<EventHandler> handlers = new ArrayList<>();
|
||||
|
||||
private ServiceTracker<IEventManager, IEventManager> serviceTracker;
|
||||
|
||||
|
@ -137,50 +138,84 @@ public abstract class AnnotationBasedEventManager extends AnnotationBasedFactory
|
|||
}
|
||||
|
||||
/**
|
||||
* Perform scan, discover and register of annotated classes
|
||||
* Scan, discover and register annotated event delegate classes. <br/>
|
||||
* The scan is asynchronous and return {@link CompletableFuture} to caller.
|
||||
* If needed, caller can use the return {@link CompletableFuture} to wait for the scan to complete (using either get or join).
|
||||
* @param context bundle context
|
||||
* @param packageNames one or more package to scan
|
||||
* @return CompletableFuture<List<EventHandler>>
|
||||
*/
|
||||
protected void scan() {
|
||||
long start = System.currentTimeMillis();
|
||||
ClassLoader classLoader = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader();
|
||||
|
||||
public synchronized CompletableFuture<List<EventHandler>> scan(BundleContext context, String ...packageNames) {
|
||||
return scan(context, false, packageNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan, discover and register annotated event delegate classes.
|
||||
* @param context bundle context
|
||||
* @param logScanDuration
|
||||
* @param packageNames one or more package to scan
|
||||
* @return CompletableFuture<List<EventHandler>>
|
||||
*/
|
||||
protected CompletableFuture<List<EventHandler>> scan(BundleContext context, boolean logScanDuration, String ...packageNames) {
|
||||
long start = logScanDuration ? System.currentTimeMillis() : 0;
|
||||
final CompletableFuture<List<EventHandler>> completable = new CompletableFuture<>();
|
||||
ClassLoader classLoader = context.getBundle().adapt(BundleWiring.class).getClassLoader();
|
||||
|
||||
ClassGraph graph = new ClassGraph()
|
||||
.enableAnnotationInfo()
|
||||
.overrideClassLoaders(classLoader)
|
||||
.disableNestedJarScanning()
|
||||
.disableModuleScanning()
|
||||
.acceptPackagesNonRecursive(getPackages());
|
||||
.acceptPackagesNonRecursive(packageNames);
|
||||
|
||||
ScanResultProcessor scanResultProcessor = scanResult ->
|
||||
{
|
||||
List<EventHandler> handlerList = new ArrayList<>();
|
||||
for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(EventTopicDelegate.class)) {
|
||||
if (classInfo.isAbstract())
|
||||
continue;
|
||||
EventHandler handler = null;
|
||||
String className = classInfo.getName();
|
||||
AnnotationInfo baseInfo = classInfo.getAnnotationInfo(EventTopicDelegate.class);
|
||||
String filter = (String) baseInfo.getParameterValues().getValue("filter");
|
||||
if (classInfo.hasAnnotation(ModelEventTopic.class)) {
|
||||
AnnotationInfo annotationInfo = classInfo.getAnnotationInfo(ModelEventTopic.class);
|
||||
modelEventDelegate(classLoader, className, annotationInfo, filter);
|
||||
handler = modelEventDelegate(classLoader, className, annotationInfo, filter);
|
||||
} else if (classInfo.hasAnnotation(ImportEventTopic.class)) {
|
||||
AnnotationInfo annotationInfo = classInfo.getAnnotationInfo(ImportEventTopic.class);
|
||||
importEventDelegate(classLoader, className, annotationInfo, filter);
|
||||
handler = importEventDelegate(classLoader, className, annotationInfo, filter);
|
||||
} else if (classInfo.hasAnnotation(ProcessEventTopic.class)) {
|
||||
AnnotationInfo annotationInfo = classInfo.getAnnotationInfo(ProcessEventTopic.class);
|
||||
processEventDelegate(classLoader, className, annotationInfo, filter);
|
||||
handler = processEventDelegate(classLoader, className, annotationInfo, filter);
|
||||
} else {
|
||||
simpleEventDelegate(classLoader, className, filter);
|
||||
handler = simpleEventDelegate(classLoader, className, filter);
|
||||
}
|
||||
if (handler != null)
|
||||
handlerList.add(handler);
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
s_log.info(() -> this.getClass().getSimpleName() + " loaded " + handlers.size() + " classes in "
|
||||
+ ((end-start)/1000f) + "s");
|
||||
signalScanCompletion(true);
|
||||
if (logScanDuration)
|
||||
s_log.info(() -> this.getClass().getSimpleName() + " loaded " + handlerList.size() + " classes in "
|
||||
+ ((end-start)/1000f) + "s");
|
||||
if (handlerList.size() > 0) {
|
||||
synchronized (handlers) {
|
||||
handlers.addAll(handlerList);
|
||||
}
|
||||
}
|
||||
completable.complete(handlerList);
|
||||
};
|
||||
|
||||
graph.scanAsync(getExecutorService(), getMaxThreads(), scanResultProcessor, getScanFailureHandler());
|
||||
return completable;
|
||||
}
|
||||
/**
|
||||
* Perform asynchronous scan, discover and register of annotated event delegate classes.
|
||||
*/
|
||||
protected void scan() {
|
||||
scan(bundleContext, true, getPackages());
|
||||
}
|
||||
|
||||
private void simpleEventDelegate(ClassLoader classLoader, String className, String filter) {
|
||||
private EventHandler simpleEventDelegate(ClassLoader classLoader, String className, String filter) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends EventDelegate> delegateClass = (Class<? extends EventDelegate>) classLoader.loadClass(className);
|
||||
|
@ -189,15 +224,16 @@ public abstract class AnnotationBasedEventManager extends AnnotationBasedFactory
|
|||
SimpleEventHandler handler = new SimpleEventHandler(delegateClass, supplier);
|
||||
if (!Util.isEmpty(filter, true))
|
||||
handler.setFilter(filter);
|
||||
handlers.add(handler);
|
||||
eventManager.register(handler.getTopics(), handler.getFilter(), handler);
|
||||
eventManager.register(handler.getTopics(), handler.getFilter(), handler);
|
||||
return handler;
|
||||
} catch (Exception e) {
|
||||
if (s_log.isLoggable(Level.INFO))
|
||||
s_log.log(Level.INFO, e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void processEventDelegate(ClassLoader classLoader, String className, AnnotationInfo annotationInfo, String filter) {
|
||||
private EventHandler processEventDelegate(ClassLoader classLoader, String className, AnnotationInfo annotationInfo, String filter) {
|
||||
try {
|
||||
String processUUID = (String) annotationInfo.getParameterValues().getValue("processUUID");
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -207,15 +243,16 @@ public abstract class AnnotationBasedEventManager extends AnnotationBasedFactory
|
|||
ProcessEventHandler handler = new ProcessEventHandler(delegateClass, processUUID, supplier);
|
||||
if (!Util.isEmpty(filter, true))
|
||||
handler.setFilter(filter);
|
||||
handlers.add(handler);
|
||||
eventManager.register(handler.getTopics(), handler.getFilter(), handler);
|
||||
return handler;
|
||||
} catch (Exception e) {
|
||||
if (s_log.isLoggable(Level.INFO))
|
||||
s_log.log(Level.INFO, e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void importEventDelegate(ClassLoader classLoader, String className, AnnotationInfo annotationInfo, String filter) {
|
||||
private EventHandler importEventDelegate(ClassLoader classLoader, String className, AnnotationInfo annotationInfo, String filter) {
|
||||
try {
|
||||
String importTableName = (String) annotationInfo.getParameterValues().getValue("importTableName");
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -225,15 +262,16 @@ public abstract class AnnotationBasedEventManager extends AnnotationBasedFactory
|
|||
ImportEventHandler handler = new ImportEventHandler(delegateClass, importTableName, supplier);
|
||||
if (!Util.isEmpty(filter, true))
|
||||
handler.setFilter(filter);
|
||||
handlers.add(handler);
|
||||
eventManager.register(handler.getTopics(), handler.getFilter(), handler);
|
||||
eventManager.register(handler.getTopics(), handler.getFilter(), handler);
|
||||
return handler;
|
||||
} catch (Exception e) {
|
||||
if (s_log.isLoggable(Level.INFO))
|
||||
s_log.log(Level.INFO, e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void modelEventDelegate(ClassLoader classLoader, String className, AnnotationInfo annotationInfo, String filter) {
|
||||
private EventHandler modelEventDelegate(ClassLoader classLoader, String className, AnnotationInfo annotationInfo, String filter) {
|
||||
try {
|
||||
AnnotationClassRef classRef = (AnnotationClassRef) annotationInfo.getParameterValues().getValue("modelClass");
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -246,11 +284,12 @@ public abstract class AnnotationBasedEventManager extends AnnotationBasedFactory
|
|||
ModelEventHandler<?> handler = new ModelEventHandler(modelClass, delegateClass, supplier);
|
||||
if (!Util.isEmpty(filter, true))
|
||||
handler.setFilter(filter);
|
||||
handlers.add(handler);
|
||||
eventManager.register(handler.getTopics(), handler.getFilter(), handler);
|
||||
return handler;
|
||||
} catch (Exception e) {
|
||||
if (s_log.isLoggable(Level.INFO))
|
||||
s_log.log(Level.INFO, e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1076,4 +1076,15 @@ public class Core {
|
|||
//fall back, should not reach here
|
||||
return new DefaultTaxLookup();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link DefaultAnnotationBasedEventManager}
|
||||
*/
|
||||
public static DefaultAnnotationBasedEventManager getDefaultAnnotationBasedEventManager() {
|
||||
IServiceReferenceHolder<DefaultAnnotationBasedEventManager> serviceReference = Service.locator().locate(DefaultAnnotationBasedEventManager.class).getServiceReference();
|
||||
if (serviceReference != null) {
|
||||
return serviceReference.getService();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ package org.adempiere.base;
|
|||
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
@Component(immediate = true, service = {})
|
||||
@Component(immediate = true, service = {DefaultAnnotationBasedEventManager.class})
|
||||
public class DefaultAnnotationBasedEventManager extends AnnotationBasedEventManager {
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/***********************************************************************
|
||||
* This file is part of iDempiere ERP Open Source *
|
||||
* http://www.idempiere.org *
|
||||
* *
|
||||
* Copyright (C) Contributors *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License *
|
||||
* as published by the Free Software Foundation; either version 2 *
|
||||
* of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301, USA. *
|
||||
* *
|
||||
* Contributors: *
|
||||
* - hengsin *
|
||||
**********************************************************************/
|
||||
package org.idempiere.test.event;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.adempiere.base.Core;
|
||||
import org.adempiere.base.DefaultAnnotationBasedEventManager;
|
||||
import org.compiere.model.MTest;
|
||||
import org.compiere.util.Env;
|
||||
import org.idempiere.test.AbstractTestCase;
|
||||
import org.idempiere.test.TestActivator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.parallel.Isolated;
|
||||
import org.osgi.service.event.EventHandler;
|
||||
|
||||
/**
|
||||
* @author hengsin
|
||||
*/
|
||||
@Isolated
|
||||
public class EventDelegateAnnotationTest extends AbstractTestCase {
|
||||
|
||||
public EventDelegateAnnotationTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotatedEventDelegate() {
|
||||
DefaultAnnotationBasedEventManager mgr = Core.getDefaultAnnotationBasedEventManager();
|
||||
CompletableFuture<List<EventHandler>> completable = mgr.scan(TestActivator.context, MTestEventDelegate.class.getPackageName());
|
||||
completable.join();
|
||||
|
||||
String desc = "test";
|
||||
MTest mtest = new MTest(Env.getCtx(), 0, getTrxName());
|
||||
mtest.setName("testAnnotatedEventDelegate");
|
||||
mtest.setDescription(desc);
|
||||
mtest.saveEx();
|
||||
|
||||
assertEquals(desc + "MTestEventDelegate", mtest.getDescription(), "MTestEventDelegate not handling before new event as expected");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/***********************************************************************
|
||||
* This file is part of iDempiere ERP Open Source *
|
||||
* http://www.idempiere.org *
|
||||
* *
|
||||
* Copyright (C) Contributors *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License *
|
||||
* as published by the Free Software Foundation; either version 2 *
|
||||
* of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301, USA. *
|
||||
* *
|
||||
* Contributors: *
|
||||
* - hengsin *
|
||||
**********************************************************************/
|
||||
package org.idempiere.test.event;
|
||||
|
||||
import org.adempiere.base.annotation.EventTopicDelegate;
|
||||
import org.adempiere.base.annotation.ModelEventTopic;
|
||||
import org.adempiere.base.event.annotations.ModelEventDelegate;
|
||||
import org.adempiere.base.event.annotations.po.BeforeChange;
|
||||
import org.adempiere.base.event.annotations.po.BeforeNew;
|
||||
import org.compiere.model.MTest;
|
||||
import org.osgi.service.event.Event;
|
||||
|
||||
/**
|
||||
* @author hengsin
|
||||
*/
|
||||
@EventTopicDelegate
|
||||
@ModelEventTopic(modelClass = MTest.class)
|
||||
public class MTestEventDelegate extends ModelEventDelegate<MTest> {
|
||||
|
||||
/**
|
||||
* @param po
|
||||
* @param event
|
||||
*/
|
||||
public MTestEventDelegate(MTest po, Event event) {
|
||||
super(po, event);
|
||||
}
|
||||
|
||||
@BeforeChange
|
||||
@BeforeNew
|
||||
public void onBeforeChange() {
|
||||
String desc = getModel().getDescription();
|
||||
if (desc != null)
|
||||
desc = desc + "MTestEventDelegate";
|
||||
else
|
||||
desc = "MTestEventDelegate";
|
||||
getModel().setDescription(desc);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue