IDEMPIERE-5019 add org.adempiere.base.annotation.EventTopicDelegate annotation (#952)

This commit is contained in:
hengsin 2021-10-28 17:21:08 +08:00 committed by GitHub
parent c4c5fed0fa
commit 28638d075b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 768 additions and 83 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
}
}

View File

@ -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"};
}
}

View File

@ -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 "";
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -30,6 +30,7 @@ import org.osgi.service.event.Event;
* *
* Annotation driven event delegate base class that works together with {@link BaseEventHandler}. * 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 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 * @author hengsin
* *
*/ */

View File

@ -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());
}
}

View File

@ -31,8 +31,11 @@ import static org.junit.jupiter.api.Assertions.fail;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.concurrent.atomic.AtomicInteger;
import org.adempiere.base.Core; 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.FactsEventData;
import org.adempiere.base.event.annotations.AfterLogin; import org.adempiere.base.event.annotations.AfterLogin;
import org.adempiere.base.event.annotations.EventDelegate; 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.MBPartner;
import org.compiere.model.MInOut; import org.compiere.model.MInOut;
import org.compiere.model.MInOutLine; import org.compiere.model.MInOutLine;
import org.compiere.model.MLocation;
import org.compiere.model.MOrder; import org.compiere.model.MOrder;
import org.compiere.model.MOrderLine; import org.compiere.model.MOrderLine;
import org.compiere.model.MOrg; import org.compiere.model.MOrg;
import org.compiere.model.MProcess; import org.compiere.model.MProcess;
import org.compiere.model.MProduct; import org.compiere.model.MProduct;
import org.compiere.model.MSysConfig;
import org.compiere.model.ModelValidationEngine; 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_BPartner;
import org.compiere.model.X_I_Product; import org.compiere.model.X_I_Product;
import org.compiere.process.DocAction; import org.compiere.process.DocAction;
@ -66,6 +73,7 @@ import org.compiere.process.ImportBPartner;
import org.compiere.process.ImportProduct; import org.compiere.process.ImportProduct;
import org.compiere.process.ProcessInfo; import org.compiere.process.ProcessInfo;
import org.compiere.process.ServerProcessCtl; import org.compiere.process.ServerProcessCtl;
import org.compiere.util.CacheMgt;
import org.compiere.util.Env; import org.compiere.util.Env;
import org.compiere.util.KeyNamePair; import org.compiere.util.KeyNamePair;
import org.compiere.util.Login; 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())); "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> { private final static class MyBPBeforeNewDelegate extends ModelEventDelegate<MBPartner> {
public MyBPBeforeNewDelegate(MBPartner bp, Event event) { public MyBPBeforeNewDelegate(MBPartner bp, Event event) {