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}.
|
* 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
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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.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) {
|
||||||
|
|
Loading…
Reference in New Issue