IDEMPIERE-5019 add org.adempiere.base.annotation.EventTopicDelegate annotation (#952)
This commit is contained in:
parent
c4c5fed0fa
commit
28638d075b
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" configuration-policy="optional" name="org.adempiere.base.event.AddressValidationEventHandler">
|
||||
<implementation class="org.adempiere.base.event.AddressValidationEventHandler"/>
|
||||
<reference bind="bindEventManager" cardinality="1..1" interface="org.adempiere.base.event.IEventManager" name="IEventManager" policy="static" unbind="unbindEventManager"/>
|
||||
</scr:component>
|
|
@ -0,0 +1,4 @@
|
|||
<?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">
|
||||
<implementation class="org.adempiere.base.DefaultAnnotationBasedEventManager"/>
|
||||
</scr:component>
|
|
@ -0,0 +1,372 @@
|
|||
/***********************************************************************
|
||||
* 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.adempiere.base;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.adempiere.base.annotation.EventTopicDelegate;
|
||||
import org.adempiere.base.annotation.ImportEventTopic;
|
||||
import org.adempiere.base.annotation.ModelEventTopic;
|
||||
import org.adempiere.base.annotation.ProcessEventTopic;
|
||||
import org.adempiere.base.event.IEventManager;
|
||||
import org.adempiere.base.event.annotations.EventDelegate;
|
||||
import org.adempiere.base.event.annotations.ModelEventDelegate;
|
||||
import org.adempiere.base.event.annotations.ModelEventHandler;
|
||||
import org.adempiere.base.event.annotations.SimpleEventHandler;
|
||||
import org.adempiere.base.event.annotations.imp.ImportEventDelegate;
|
||||
import org.adempiere.base.event.annotations.imp.ImportEventHandler;
|
||||
import org.adempiere.base.event.annotations.process.ProcessEventDelegate;
|
||||
import org.adempiere.base.event.annotations.process.ProcessEventHandler;
|
||||
import org.compiere.model.PO;
|
||||
import org.compiere.util.CLogger;
|
||||
import org.compiere.util.Util;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
import org.osgi.framework.wiring.BundleWiring;
|
||||
import org.osgi.service.component.ComponentContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.osgi.service.event.Event;
|
||||
import org.osgi.service.event.EventHandler;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
import org.osgi.util.tracker.ServiceTrackerCustomizer;
|
||||
|
||||
import io.github.classgraph.AnnotationClassRef;
|
||||
import io.github.classgraph.AnnotationInfo;
|
||||
import io.github.classgraph.ClassGraph;
|
||||
import io.github.classgraph.ClassInfo;
|
||||
import io.github.classgraph.ScanResult;
|
||||
|
||||
/**
|
||||
* Scan, discover and register classes with {@link EventTopicDelegate} annotation
|
||||
* @author hengsin
|
||||
*
|
||||
*/
|
||||
public abstract class AnnotationBasedEventManager {
|
||||
|
||||
private static final CLogger s_log = CLogger.getCLogger(AnnotationBasedEventManager.class);
|
||||
|
||||
private IEventManager eventManager;
|
||||
private BundleContext bundleContext;
|
||||
private List<EventHandler> handlers = new ArrayList<>();
|
||||
|
||||
private ServiceTracker<IEventManager, IEventManager> serviceTracker;
|
||||
|
||||
/**
|
||||
* default constructor
|
||||
*/
|
||||
public AnnotationBasedEventManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass would override this to define the list of packages to perform the scan, discover and register operation
|
||||
* @return packages to scan
|
||||
*/
|
||||
public abstract String[] getPackages();
|
||||
|
||||
@Activate
|
||||
public void activate(ComponentContext context) {
|
||||
bundleContext = context.getBundleContext();
|
||||
serviceTracker = new ServiceTracker<IEventManager, IEventManager>(bundleContext, IEventManager.class,
|
||||
new Customizer());
|
||||
serviceTracker.open();
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
public void deactivate(ComponentContext context) {
|
||||
if (serviceTracker != null) {
|
||||
serviceTracker.close();
|
||||
serviceTracker = null;
|
||||
}
|
||||
if (eventManager != null) {
|
||||
if (handlers.size() > 0)
|
||||
unbindService(eventManager);
|
||||
eventManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param eventManager
|
||||
*/
|
||||
protected void bindService(IEventManager eventManager) {
|
||||
this.eventManager = eventManager;
|
||||
if (eventManager != null)
|
||||
scan();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param eventManager
|
||||
*/
|
||||
protected void unbindService(IEventManager eventManager) {
|
||||
if (eventManager != null && eventManager == this.eventManager) {
|
||||
if (handlers.size() > 0) {
|
||||
for(EventHandler handler : handlers)
|
||||
eventManager.unregister(handler);
|
||||
}
|
||||
this.eventManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform scan, discover and register of annotated classes
|
||||
*/
|
||||
protected void scan() {
|
||||
long start = System.currentTimeMillis();
|
||||
ClassLoader classLoader = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader();
|
||||
|
||||
ClassGraph graph = new ClassGraph()
|
||||
.enableAnnotationInfo()
|
||||
.overrideClassLoaders(classLoader)
|
||||
.disableNestedJarScanning()
|
||||
.disableModuleScanning()
|
||||
.acceptPackagesNonRecursive(getPackages());
|
||||
|
||||
try (ScanResult scanResult = graph.scan())
|
||||
{
|
||||
for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(EventTopicDelegate.class)) {
|
||||
if (classInfo.isAbstract())
|
||||
continue;
|
||||
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);
|
||||
} else if (classInfo.hasAnnotation(ImportEventTopic.class)) {
|
||||
AnnotationInfo annotationInfo = classInfo.getAnnotationInfo(ImportEventTopic.class);
|
||||
importEventDelegate(classLoader, className, annotationInfo, filter);
|
||||
} else if (classInfo.hasAnnotation(ProcessEventTopic.class)) {
|
||||
AnnotationInfo annotationInfo = classInfo.getAnnotationInfo(ProcessEventTopic.class);
|
||||
processEventDelegate(classLoader, className, annotationInfo, filter);
|
||||
} else {
|
||||
simpleEventDelegate(classLoader, className, filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
if (s_log.isLoggable(Level.INFO))
|
||||
s_log.info(this.getClass().getSimpleName() + " loaded "+handlers.size() +" classes in "
|
||||
+((end-start)/1000f) + "s");
|
||||
|
||||
}
|
||||
|
||||
private void simpleEventDelegate(ClassLoader classLoader, String className, String filter) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends EventDelegate> delegateClass = (Class<? extends EventDelegate>) classLoader.loadClass(className);
|
||||
Constructor<?> constructor = delegateClass.getConstructor(Event.class);
|
||||
EventDelegateSupplier supplier = new EventDelegateSupplier(constructor);
|
||||
SimpleEventHandler handler = new SimpleEventHandler(delegateClass, supplier);
|
||||
if (!Util.isEmpty(filter, true))
|
||||
handler.setFilter(filter);
|
||||
handlers.add(handler);
|
||||
eventManager.register(handler.getTopics(), handler.getFilter(), handler);
|
||||
} catch (Exception e) {
|
||||
if (s_log.isLoggable(Level.INFO))
|
||||
s_log.log(Level.INFO, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void processEventDelegate(ClassLoader classLoader, String className, AnnotationInfo annotationInfo, String filter) {
|
||||
try {
|
||||
String processUUID = (String) annotationInfo.getParameterValues().getValue("processUUID");
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends ProcessEventDelegate> delegateClass = (Class<? extends ProcessEventDelegate>) classLoader.loadClass(className);
|
||||
Constructor<?> constructor = delegateClass.getConstructor(Event.class);
|
||||
ProcessDelegateSupplier supplier = new ProcessDelegateSupplier(constructor);
|
||||
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);
|
||||
} catch (Exception e) {
|
||||
if (s_log.isLoggable(Level.INFO))
|
||||
s_log.log(Level.INFO, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void importEventDelegate(ClassLoader classLoader, String className, AnnotationInfo annotationInfo, String filter) {
|
||||
try {
|
||||
String importTableName = (String) annotationInfo.getParameterValues().getValue("importTableName");
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends ImportEventDelegate> delegateClass = (Class<? extends ImportEventDelegate>) classLoader.loadClass(className);
|
||||
Constructor<?> constructor = delegateClass.getConstructor(Event.class);
|
||||
ImportDelegateSupplier supplier = new ImportDelegateSupplier(constructor);
|
||||
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);
|
||||
} catch (Exception e) {
|
||||
if (s_log.isLoggable(Level.INFO))
|
||||
s_log.log(Level.INFO, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void modelEventDelegate(ClassLoader classLoader, String className, AnnotationInfo annotationInfo, String filter) {
|
||||
try {
|
||||
AnnotationClassRef classRef = (AnnotationClassRef) annotationInfo.getParameterValues().getValue("modelClass");
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends PO> modelClass = (Class<? extends PO>)classRef.loadClass();
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends ModelEventDelegate<? extends PO>> delegateClass = (Class<ModelEventDelegate<? extends PO>>) classLoader.loadClass(className);
|
||||
Constructor<?> constructor = delegateClass.getDeclaredConstructor(modelClass, Event.class);
|
||||
ModelDelegateSupplier supplier = new ModelDelegateSupplier(constructor);
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
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);
|
||||
} catch (Exception e) {
|
||||
if (s_log.isLoggable(Level.INFO))
|
||||
s_log.log(Level.INFO, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private class Customizer implements ServiceTrackerCustomizer<IEventManager, IEventManager> {
|
||||
@Override
|
||||
public IEventManager addingService(ServiceReference<IEventManager> reference) {
|
||||
IEventManager eventManager = bundleContext.getService(reference);
|
||||
bindService(eventManager);
|
||||
return eventManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifiedService(ServiceReference<IEventManager> reference, IEventManager service) {
|
||||
if (eventManager != null && eventManager != service) {
|
||||
unbindService(eventManager);
|
||||
bindService(service);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removedService(ServiceReference<IEventManager> reference, IEventManager service) {
|
||||
unbindService(service);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ModelDelegateSupplier implements BiFunction<PO, Event, ModelEventDelegate<?>> {
|
||||
|
||||
private Constructor<?> constructor;
|
||||
|
||||
private ModelDelegateSupplier(Constructor<?> constructor) {
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelEventDelegate<?> apply(PO t, Event u) {
|
||||
ModelEventDelegate<?> delegate = null;
|
||||
if (constructor != null) {
|
||||
try {
|
||||
delegate = (ModelEventDelegate<?>) constructor.newInstance(t, u);
|
||||
} catch (Exception e) {
|
||||
constructor = null;
|
||||
s_log.log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return delegate;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ImportDelegateSupplier implements Function<Event, ImportEventDelegate> {
|
||||
|
||||
private Constructor<?> constructor;
|
||||
|
||||
private ImportDelegateSupplier(Constructor<?> constructor) {
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportEventDelegate apply(Event t) {
|
||||
ImportEventDelegate delegate = null;
|
||||
if (constructor != null) {
|
||||
try {
|
||||
delegate = (ImportEventDelegate) constructor.newInstance(t);
|
||||
} catch (Exception e) {
|
||||
constructor = null;
|
||||
s_log.log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return delegate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ProcessDelegateSupplier implements Function<Event, ProcessEventDelegate> {
|
||||
|
||||
private Constructor<?> constructor;
|
||||
|
||||
private ProcessDelegateSupplier(Constructor<?> constructor) {
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessEventDelegate apply(Event t) {
|
||||
ProcessEventDelegate delegate = null;
|
||||
if (constructor != null) {
|
||||
try {
|
||||
delegate = (ProcessEventDelegate) constructor.newInstance(t);
|
||||
} catch (Exception e) {
|
||||
constructor = null;
|
||||
s_log.log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return delegate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class EventDelegateSupplier implements Function<Event, EventDelegate> {
|
||||
|
||||
private Constructor<?> constructor;
|
||||
|
||||
private EventDelegateSupplier(Constructor<?> constructor) {
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventDelegate apply(Event t) {
|
||||
EventDelegate delegate = null;
|
||||
if (constructor != null) {
|
||||
try {
|
||||
delegate = (EventDelegate) constructor.newInstance(t);
|
||||
} catch (Exception e) {
|
||||
constructor = null;
|
||||
s_log.log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return delegate;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/***********************************************************************
|
||||
* 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.adempiere.base;
|
||||
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
@Component(immediate = true, service = {})
|
||||
public class DefaultAnnotationBasedEventManager extends AnnotationBasedEventManager {
|
||||
|
||||
/**
|
||||
* default constructor
|
||||
*/
|
||||
public DefaultAnnotationBasedEventManager() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPackages() {
|
||||
return new String[] {"org.adempiere.base.event.delegate"};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/***********************************************************************
|
||||
* 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.adempiere.base.annotation;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.adempiere.base.event.annotations.EventDelegate;
|
||||
|
||||
/**
|
||||
*
|
||||
* Annotation for OSGi Event Topic Delegate.
|
||||
* Works with {@link EventDelegate} and its sub classes
|
||||
* @author hengsin
|
||||
*
|
||||
*/
|
||||
@Retention(RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface EventTopicDelegate {
|
||||
/**
|
||||
* Optional event topic filter
|
||||
* @return filter
|
||||
*/
|
||||
String filter() default "";
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/***********************************************************************
|
||||
* 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.adempiere.base.annotation;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.adempiere.base.event.annotations.imp.ImportEventDelegate;
|
||||
|
||||
/**
|
||||
* Define parameter for {@link ImportEventDelegate}
|
||||
* Works with classes with {@link EventTopicDelegate} annotation
|
||||
* @author hengsin
|
||||
*
|
||||
*/
|
||||
@Retention(RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ImportEventTopic {
|
||||
/**
|
||||
* Import table (I_*) name for {@link ImportEventDelegate}
|
||||
* @return import table name
|
||||
*/
|
||||
String importTableName();
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/***********************************************************************
|
||||
* 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.adempiere.base.annotation;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.adempiere.base.event.annotations.ModelEventDelegate;
|
||||
import org.compiere.model.PO;
|
||||
|
||||
/**
|
||||
* Specify parameter for {@link ModelEventDelegate}
|
||||
* Works with classes with {@link EventTopicDelegate} annotation
|
||||
* @author hengsin
|
||||
*
|
||||
*/
|
||||
@Retention(RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ModelEventTopic {
|
||||
/**
|
||||
* Model class (M* or X_*) for {@link ModelEventDelegate}
|
||||
* @return model class
|
||||
*/
|
||||
Class<? extends PO> modelClass();
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/***********************************************************************
|
||||
* 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.adempiere.base.annotation;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.adempiere.base.event.annotations.process.ProcessEventDelegate;
|
||||
|
||||
/**
|
||||
* Define parameter for {@link ProcessEventDelegate}
|
||||
* Works with classes with {@link EventTopicDelegate} annotation
|
||||
* @author hengsin
|
||||
*
|
||||
*/
|
||||
@Retention(RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ProcessEventTopic {
|
||||
/**
|
||||
* AD_Process.AD_Process_UU (uuid) value for {@link ProcessEventDelegate}
|
||||
* @return process uuid
|
||||
*/
|
||||
String processUUID();
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/******************************************************************************
|
||||
* Copyright (C) 2013 Elaine Tan *
|
||||
* Copyright (C) 2013 Trek Global
|
||||
* This program is free software; you can redistribute it and/or modify it *
|
||||
* under the terms version 2 of the GNU General Public License as published *
|
||||
* by the Free Software Foundation. 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., *
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
|
||||
*****************************************************************************/
|
||||
package org.adempiere.base.event;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.compiere.model.I_C_Location;
|
||||
import org.compiere.model.MAddressValidation;
|
||||
import org.compiere.model.MLocation;
|
||||
import org.compiere.model.MSysConfig;
|
||||
import org.compiere.model.PO;
|
||||
import org.osgi.service.event.Event;
|
||||
|
||||
/**
|
||||
* Address validation event handler
|
||||
* @author Elaine
|
||||
*
|
||||
*/
|
||||
public class AddressValidationEventHandler extends AbstractEventHandler {
|
||||
|
||||
@Override
|
||||
protected void doHandleEvent(Event event) {
|
||||
String topic = event.getTopic();
|
||||
if (topic.equals(IEventTopics.PO_BEFORE_NEW) || topic.equals(IEventTopics.PO_BEFORE_CHANGE))
|
||||
{
|
||||
PO po = getPO(event);
|
||||
if (po.get_TableName().equals(I_C_Location.Table_Name))
|
||||
{
|
||||
MLocation location = (MLocation) po;
|
||||
|
||||
String addressValidation = MSysConfig.getValue(MSysConfig.ADDRESS_VALIDATION, null, location.getAD_Client_ID());
|
||||
boolean isEnabled = false;
|
||||
if (addressValidation != null && addressValidation.trim().length() > 0 && location.getCountry() != null)
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(addressValidation, ";");
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
String token = st.nextToken().trim();
|
||||
if (token.equals(location.getCountry().getCountryCode().trim()))
|
||||
{
|
||||
isEnabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isEnabled)
|
||||
return;
|
||||
|
||||
MAddressValidation validation = null;
|
||||
if (location.getC_AddressValidation_ID() > 0)
|
||||
validation = new MAddressValidation(location.getCtx(), location.getC_AddressValidation_ID(), location.get_TrxName());
|
||||
if (validation == null)
|
||||
validation = MAddressValidation.getDefaultAddressValidation(location.getCtx(), location.getAD_Client_ID(), location.get_TrxName());
|
||||
if (validation != null)
|
||||
location.processOnline(validation.getC_AddressValidation_ID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
registerTableEvent(IEventTopics.PO_BEFORE_NEW, I_C_Location.Table_Name);
|
||||
registerTableEvent(IEventTopics.PO_BEFORE_CHANGE, I_C_Location.Table_Name);
|
||||
}
|
||||
|
||||
}
|
|
@ -30,6 +30,7 @@ import org.osgi.service.event.Event;
|
|||
*
|
||||
* Annotation driven event delegate base class that works together with {@link BaseEventHandler}.
|
||||
* Subclass implementation doesn't have to be thread safe as event delegate is create and throw away for each event call.
|
||||
* Subclass should use {@link EventTopic} or one of its derived annotation to define the event topic to handle
|
||||
* @author hengsin
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/***********************************************************************
|
||||
* 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.adempiere.base.event.delegate;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
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.MAddressValidation;
|
||||
import org.compiere.model.MLocation;
|
||||
import org.compiere.model.MSysConfig;
|
||||
import org.osgi.service.event.Event;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hengsin
|
||||
*
|
||||
*/
|
||||
@EventTopicDelegate
|
||||
@ModelEventTopic(modelClass = MLocation.class)
|
||||
public class AddressValidationEventDelegate extends ModelEventDelegate<MLocation> {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param po
|
||||
* @param event
|
||||
*/
|
||||
public AddressValidationEventDelegate(MLocation po, Event event) {
|
||||
super(po, event);
|
||||
}
|
||||
|
||||
@BeforeNew
|
||||
@BeforeChange
|
||||
public void beforeCreateOrUpdate() {
|
||||
MLocation location = getModel();
|
||||
|
||||
//for unit test checking (see org.idempiere.test.event.EventHandlerTest#testAddressValidationDelegate)
|
||||
if (getEvent().containsProperty(getClass().getName())) {
|
||||
Object value = getEvent().getProperty(getClass().getName());
|
||||
if (value instanceof AtomicInteger) {
|
||||
((AtomicInteger) value).set(1);
|
||||
}
|
||||
}
|
||||
|
||||
String addressValidation = MSysConfig.getValue(MSysConfig.ADDRESS_VALIDATION, null, location.getAD_Client_ID());
|
||||
boolean isEnabled = false;
|
||||
if (addressValidation != null && addressValidation.trim().length() > 0 && location.getCountry() != null)
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(addressValidation, ";");
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
String token = st.nextToken().trim();
|
||||
if (token.equals(location.getCountry().getCountryCode().trim()))
|
||||
{
|
||||
isEnabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isEnabled)
|
||||
return;
|
||||
|
||||
MAddressValidation validation = null;
|
||||
if (location.getC_AddressValidation_ID() > 0)
|
||||
validation = new MAddressValidation(location.getCtx(), location.getC_AddressValidation_ID(), location.get_TrxName());
|
||||
if (validation == null)
|
||||
validation = MAddressValidation.getDefaultAddressValidation(location.getCtx(), location.getAD_Client_ID(), location.get_TrxName());
|
||||
if (validation != null)
|
||||
location.processOnline(validation.getC_AddressValidation_ID());
|
||||
}
|
||||
}
|
|
@ -31,8 +31,11 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.adempiere.base.Core;
|
||||
import org.adempiere.base.event.EventManager;
|
||||
import org.adempiere.base.event.EventProperty;
|
||||
import org.adempiere.base.event.FactsEventData;
|
||||
import org.adempiere.base.event.annotations.AfterLogin;
|
||||
import org.adempiere.base.event.annotations.EventDelegate;
|
||||
|
@ -52,12 +55,16 @@ import org.adempiere.model.ImportValidator;
|
|||
import org.compiere.model.MBPartner;
|
||||
import org.compiere.model.MInOut;
|
||||
import org.compiere.model.MInOutLine;
|
||||
import org.compiere.model.MLocation;
|
||||
import org.compiere.model.MOrder;
|
||||
import org.compiere.model.MOrderLine;
|
||||
import org.compiere.model.MOrg;
|
||||
import org.compiere.model.MProcess;
|
||||
import org.compiere.model.MProduct;
|
||||
import org.compiere.model.MSysConfig;
|
||||
import org.compiere.model.ModelValidationEngine;
|
||||
import org.compiere.model.ModelValidator;
|
||||
import org.compiere.model.PO;
|
||||
import org.compiere.model.X_I_BPartner;
|
||||
import org.compiere.model.X_I_Product;
|
||||
import org.compiere.process.DocAction;
|
||||
|
@ -66,6 +73,7 @@ import org.compiere.process.ImportBPartner;
|
|||
import org.compiere.process.ImportProduct;
|
||||
import org.compiere.process.ProcessInfo;
|
||||
import org.compiere.process.ServerProcessCtl;
|
||||
import org.compiere.util.CacheMgt;
|
||||
import org.compiere.util.Env;
|
||||
import org.compiere.util.KeyNamePair;
|
||||
import org.compiere.util.Login;
|
||||
|
@ -315,6 +323,50 @@ public class EventHandlerTest extends AbstractTestCase {
|
|||
"MyAfterImportDelegate not call. context="+Env.getContext(Env.getCtx(), MyAfterImportDelegate.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(8)
|
||||
public void testAddressValidationDelegate() {
|
||||
int addressValidationSysConfigId = 200033;
|
||||
String delegateName = "org.adempiere.base.event.delegate.AddressValidationEventDelegate";
|
||||
MSysConfig sysconfig = new MSysConfig(Env.getCtx(), addressValidationSysConfigId, null);
|
||||
String currentValue = sysconfig.getValue();
|
||||
try {
|
||||
try {
|
||||
PO.setCrossTenantSafe();
|
||||
sysconfig.setValue("US");
|
||||
sysconfig.saveEx();
|
||||
} finally {
|
||||
PO.clearCrossTenantSafe();
|
||||
}
|
||||
|
||||
CacheMgt.get().reset();
|
||||
|
||||
MLocation location = new MLocation(Env.getCtx(), 0, getTrxName());
|
||||
location.setC_Country_ID(100);
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
Event event = EventManager.newEvent(ModelValidator.tableEventTopics[ModelValidator.TYPE_BEFORE_NEW],
|
||||
new EventProperty(EventManager.EVENT_DATA, location), new EventProperty(EventManager.TABLE_NAME_PROPERTY, location.get_TableName()),
|
||||
new EventProperty(delegateName, count));
|
||||
EventManager.getInstance().sendEvent(event);
|
||||
assertTrue(count.get()==1, "AddressValidationEventDelegate not call for MLocation Before New Event");
|
||||
|
||||
count = new AtomicInteger(0);
|
||||
event = EventManager.newEvent(ModelValidator.tableEventTopics[ModelValidator.TYPE_BEFORE_CHANGE],
|
||||
new EventProperty(EventManager.EVENT_DATA, location), new EventProperty(EventManager.TABLE_NAME_PROPERTY, location.get_TableName()),
|
||||
new EventProperty(delegateName, count));
|
||||
EventManager.getInstance().sendEvent(event);
|
||||
assertTrue(count.get()==1, "AddressValidationEventDelegate not call for MLocation Before Change Event");
|
||||
} finally {
|
||||
try {
|
||||
PO.setCrossTenantSafe();
|
||||
sysconfig.setValue(currentValue);
|
||||
sysconfig.saveEx();
|
||||
} finally {
|
||||
PO.clearCrossTenantSafe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final static class MyBPBeforeNewDelegate extends ModelEventDelegate<MBPartner> {
|
||||
|
||||
public MyBPBeforeNewDelegate(MBPartner bp, Event event) {
|
||||
|
|
Loading…
Reference in New Issue