From 0f263f1c79f11406cf0cb450cf439a224eaba974 Mon Sep 17 00:00:00 2001 From: hengsin Date: Mon, 1 Feb 2021 22:29:13 +0800 Subject: [PATCH] =?UTF-8?q?IDEMPIERE-4675=20Add=20model=20factory=20base?= =?UTF-8?q?=20class=20backed=20by=20Map=20and=20Lambda=20=E2=80=A6=20(#555?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - added interface and default mapped model factory service - added unit test for default mapped model factory service --- org.adempiere.base/.project | 5 + .../org.eclipse.pde.ds.annotations.prefs | 8 ++ org.adempiere.base/META-INF/MANIFEST.MF | 1 + ...org.idempiere.model.MappedModelFactory.xml | 9 ++ .../src/org/adempiere/base/Core.java | 23 +++- .../idempiere/model/IMappedModelFactory.java | 57 ++++++++ .../idempiere/model/MappedModelFactory.java | 88 ++++++++++++ .../test/model/MappedModelFactoryTest.java | 126 ++++++++++++++++++ 8 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 org.adempiere.base/.settings/org.eclipse.pde.ds.annotations.prefs create mode 100644 org.adempiere.base/OSGI-INF/org.idempiere.model.MappedModelFactory.xml create mode 100644 org.adempiere.base/src/org/idempiere/model/IMappedModelFactory.java create mode 100644 org.adempiere.base/src/org/idempiere/model/MappedModelFactory.java create mode 100644 org.idempiere.test/src/org/idempiere/test/model/MappedModelFactoryTest.java diff --git a/org.adempiere.base/.project b/org.adempiere.base/.project index 1b2b28ba7c..ca3f65e8c2 100644 --- a/org.adempiere.base/.project +++ b/org.adempiere.base/.project @@ -25,6 +25,11 @@ + + org.eclipse.pde.ds.core.builder + + + org.eclipse.m2e.core.maven2Nature diff --git a/org.adempiere.base/.settings/org.eclipse.pde.ds.annotations.prefs b/org.adempiere.base/.settings/org.eclipse.pde.ds.annotations.prefs new file mode 100644 index 0000000000..73a356b6d0 --- /dev/null +++ b/org.adempiere.base/.settings/org.eclipse.pde.ds.annotations.prefs @@ -0,0 +1,8 @@ +classpath=true +dsVersion=V1_3 +eclipse.preferences.version=1 +enabled=true +generateBundleActivationPolicyLazy=true +path=OSGI-INF +validationErrorLevel=error +validationErrorLevel.missingImplicitUnbindMethod=error diff --git a/org.adempiere.base/META-INF/MANIFEST.MF b/org.adempiere.base/META-INF/MANIFEST.MF index e73dc7663f..057766f538 100644 --- a/org.adempiere.base/META-INF/MANIFEST.MF +++ b/org.adempiere.base/META-INF/MANIFEST.MF @@ -90,6 +90,7 @@ Import-Package: com.google.zxing, org.osgi.framework, org.osgi.service.cm;version="1.3.0", org.osgi.service.component;version="1.1.0", + org.osgi.service.component.annotations;version="1.3.0", org.osgi.service.component.runtime;version="1.3.0", org.osgi.service.component.runtime.dto;version="1.3.0", org.osgi.service.event;version="1.2.0", diff --git a/org.adempiere.base/OSGI-INF/org.idempiere.model.MappedModelFactory.xml b/org.adempiere.base/OSGI-INF/org.idempiere.model.MappedModelFactory.xml new file mode 100644 index 0000000000..3f7496474e --- /dev/null +++ b/org.adempiere.base/OSGI-INF/org.idempiere.model.MappedModelFactory.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base/src/org/adempiere/base/Core.java b/org.adempiere.base/src/org/adempiere/base/Core.java index 0545279038..6f8c259ade 100644 --- a/org.adempiere.base/src/org/adempiere/base/Core.java +++ b/org.adempiere.base/src/org/adempiere/base/Core.java @@ -55,6 +55,7 @@ import org.idempiere.distributed.IMessageService; import org.idempiere.fa.service.api.DepreciationFactoryLookupDTO; import org.idempiere.fa.service.api.IDepreciationMethod; import org.idempiere.fa.service.api.IDepreciationMethodFactory; +import org.idempiere.model.IMappedModelFactory; /** * This is a facade class for the Service Locator. @@ -923,5 +924,25 @@ public class Core { } return ids; } - + + private static IServiceReferenceHolder s_mappedModelFactoryReference = null; + + /** + * + * @return {@link IMappedModelFactory} + */ + public static IMappedModelFactory getMappedModelFactory(){ + IMappedModelFactory modelFactoryService = null; + if (s_mappedModelFactoryReference != null) { + modelFactoryService = s_mappedModelFactoryReference.getService(); + if (modelFactoryService != null) + return modelFactoryService; + } + IServiceReferenceHolder serviceReference = Service.locator().locate(IMappedModelFactory.class).getServiceReference(); + if (serviceReference != null) { + modelFactoryService = serviceReference.getService(); + s_mappedModelFactoryReference = serviceReference; + } + return modelFactoryService; + } } diff --git a/org.adempiere.base/src/org/idempiere/model/IMappedModelFactory.java b/org.adempiere.base/src/org/idempiere/model/IMappedModelFactory.java new file mode 100644 index 0000000000..d5623a1b94 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/model/IMappedModelFactory.java @@ -0,0 +1,57 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.idempiere.model; + +import java.sql.ResultSet; +import java.util.function.BiFunction; +import java.util.function.Supplier; + +import org.compiere.model.PO; + +/** + * + * @author hengsin + * + */ +public interface IMappedModelFactory { + + /** + * add table name to class mapping + * @param tableName + * @param classSupplier + * @param recordIdFunction + * @param resultSetFunction + */ + void addMapping(String tableName, Supplier> classSupplier, + BiFunction recordIdFunction, + BiFunction resultSetFunction); + + /** + * remove table name to class mapping + * @param tableName + */ + void removeMapping(String tableName); + +} \ No newline at end of file diff --git a/org.adempiere.base/src/org/idempiere/model/MappedModelFactory.java b/org.adempiere.base/src/org/idempiere/model/MappedModelFactory.java new file mode 100644 index 0000000000..6f36bd1505 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/model/MappedModelFactory.java @@ -0,0 +1,88 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.idempiere.model; + +import java.sql.ResultSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiFunction; +import java.util.function.Supplier; + +import org.adempiere.base.IModelFactory; +import org.compiere.model.PO; +import org.osgi.service.component.annotations.Component; + +/** + * @author hengsin + * + */ +@Component(name = "org.idempiere.model.MappedModelFactory", + immediate = true, + service = {IModelFactory.class, IMappedModelFactory.class}, + property = {"service.ranking:Integer=1"}) +public class MappedModelFactory implements IModelFactory, IMappedModelFactory { + + private final ConcurrentHashMap>> classMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> recordIdMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> resultSetMap = new ConcurrentHashMap<>(); + + /** + * default constructor + */ + public MappedModelFactory() { + } + + @Override + public Class getClass(String tableName) { + var supplier = classMap.get(tableName); + return supplier != null ? supplier.get() : null; + } + + @Override + public PO getPO(String tableName, int Record_ID, String trxName) { + var function = recordIdMap.get(tableName); + return function != null ? function.apply(Record_ID, trxName) : null; + } + + @Override + public PO getPO(String tableName, ResultSet rs, String trxName) { + var function = resultSetMap.get(tableName); + return function != null ? function.apply(rs, trxName) : null; + } + + @Override + public void addMapping(String tableName, Supplier> classSupplier, BiFunction recordIdFunction, + BiFunction resultSetFunction) { + classMap.put(tableName, classSupplier); + recordIdMap.put(tableName, recordIdFunction); + resultSetMap.put(tableName, resultSetFunction); + } + + @Override + public void removeMapping(String tableName) { + classMap.remove(tableName); + recordIdMap.remove(tableName); + resultSetMap.remove(tableName); + } +} diff --git a/org.idempiere.test/src/org/idempiere/test/model/MappedModelFactoryTest.java b/org.idempiere.test/src/org/idempiere/test/model/MappedModelFactoryTest.java new file mode 100644 index 0000000000..7541d299c6 --- /dev/null +++ b/org.idempiere.test/src/org/idempiere/test/model/MappedModelFactoryTest.java @@ -0,0 +1,126 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.idempiere.test.model; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.ResultSet; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.Properties; + +import org.adempiere.base.Core; +import org.adempiere.base.IModelFactory; +import org.compiere.model.MTable; +import org.compiere.model.PO; +import org.compiere.model.X_Test; +import org.compiere.util.CacheMgt; +import org.compiere.util.Env; +import org.idempiere.model.IMappedModelFactory; +import org.idempiere.model.MappedModelFactory; +import org.idempiere.test.AbstractTestCase; +import org.idempiere.test.TestActivator; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.osgi.framework.BundleContext; + +/** + * @author hengsin + * + */ +@TestMethodOrder(OrderAnnotation.class) +public class MappedModelFactoryTest extends AbstractTestCase { + + /** + * + */ + public MappedModelFactoryTest() { + } + + @Test + @Order(1) + public void testDefaultMappedModelFactory() { + IMappedModelFactory mappedFactory = Core.getMappedModelFactory(); + mappedFactory.addMapping(MyTest.Table_Name, () -> MyTest.class, (id, trxName) -> new MyTest(Env.getCtx(), id, trxName), + (rs, trxName) -> new MyTest(Env.getCtx(), rs, trxName)); + PO po = MTable.get(MyTest.Table_ID).getPO(0, getTrxName()); + assertTrue(po instanceof MyTest, "PO not instanceof MyTest. PO.className="+po.getClass().getName()); + } + + @Test + @Order(2) + public void testCustomMappedModelFactory() { + BundleContext bc = TestActivator.context; + Dictionary properties = new Hashtable(); + properties.put("service.ranking", Integer.valueOf(2)); + bc.registerService(IModelFactory.class, new MyFactory(), properties); + CacheMgt.get().reset(); + PO po = MTable.get(MyTest2.Table_ID).getPO(0, getTrxName()); + assertTrue(po instanceof MyTest2, "PO not instanceof MyTest2. PO.className="+po.getClass().getName()); + } + + private final static class MyFactory extends MappedModelFactory { + + public MyFactory() { + addMapping(MyTest2.Table_Name, () -> MyTest2.class, (id, trxName) -> new MyTest2(Env.getCtx(), id, trxName), + (rs, trxName) -> new MyTest2(Env.getCtx(), rs, trxName)); + } + + } + + private final static class MyTest extends X_Test { + + /** + * + */ + private static final long serialVersionUID = 2010413233032792416L; + + public MyTest(Properties ctx, int Test_ID, String trxName) { + super(ctx, Test_ID, trxName); + } + + public MyTest(Properties ctx, ResultSet rs, String trxName) { + super(ctx, rs, trxName); + } + } + + private final static class MyTest2 extends X_Test { + + /** + * + */ + private static final long serialVersionUID = 2010413233032792416L; + + public MyTest2(Properties ctx, int Test_ID, String trxName) { + super(ctx, Test_ID, trxName); + } + + public MyTest2(Properties ctx, ResultSet rs, String trxName) { + super(ctx, rs, trxName); + } + } +}