NF9 OSGi New Model Factory

From iDempiere en

Feature: OSGi New Model Factory and Model Annotations

Goal: Development

Developer: Hengsin, Saulo Gil

Description

  • Implement a new model factory base class that's backed by Map and Lambda functional object.
  • Add org.adempiere.base.Model annotation and factory class that scan and register model classes with Model annotation

Usage

With following model class:

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

Developer can use one of the following approach:

1. At plugin Activator start method, register the model class.

public void start(BundleContext context) throws Exception {
    var factory = Core.getMappedModelFactory();
    factory.addMapping(MyTest.Table_Name, () -> MyTest.class, 
          (id, trxName) -> new MyTest(Env.getCtx(), id, trxName), 
          (rs, trxName) -> new MyTest(Env.getCtx(), rs, trxName));
}

2. Create an osgi component, at the bind method for the IMappedModelFactory service.

public void bindService(IMappedModelFactory factory) {
    factory.addMapping(MyTest.Table_Name, () -> MyTest.class, 
          (id, trxName) -> new MyTest(Env.getCtx(), id, trxName), 
          (rs, trxName) -> new MyTest(Env.getCtx(), rs, trxName));
}

3. Create a subclass of MappedModelFactory, register as IModelFactory service (DO NOT register as IMappedModelFactory service).

public class MyFactory extends MappedModelFactory {	
    public MyFactory() {
        addMapping(MyTest.Table_Name, () -> MyTest.class, 
            (id, trxName) -> new MyTest(Env.getCtx(), id, trxName), 
            (rs, trxName) -> new MyTest(Env.getCtx(), rs, trxName));
    }		
}

4. IDEMPIERE-5004 added scan(BundleContext context, String... packages) method to IMappedModelFactory and MappedModelFactory. Developer can call that at plugin Activator start method to register model classes with org.adempiere.base.Model annotation.

  public void start(BundleContext context) throws Exception {
      //replace org.idempiere.test.model.annotated with the package name of your model classes
      Core.getMappedModelFactory().scan(context, "org.idempiere.test.model.annotated");
  }

The approach above is not 100% safe as Core.getMappedModelFactory() can return null if your bundle get activated before the activation of the IMappedModelFactory service. The better way is to use @Component, @Reference and @Activator instead.

 @Component(immediate = true)
 public class MyActivator implements BundleActivator {
       
       @Reference(service = IMappedModelFactory.class, cardinality = ReferenceCardinality.MANDATORY) 
       private IMappedModelFactory mappedModelFactory;
       
       public MyActivator() {
       }
       
       @Override
       public void start(BundleContext context) throws Exception {
       }
       
       @Override
       public void stop(BundleContext context) throws Exception {
       }
       
       //activate will only be called after the injection of mappedModelFactory reference
       @Activate
       public void activate(BundleContext context) {
          //replace org.idempiere.test.model.annotated with the package name of your model classes
          mappedModelFactory.scan(context, "org.idempiere.test.model.annotated");
       }
 }

Annotations

1. org.adempiere.base.Model annotation with table attribute.

2. org.adempiere.base.AnnotationBasedModelFactory, new IModelFactory service that scan and register core model classes with Model annotation. Developer only have to annotate X_* model classes as the service will looks for the lowest subclass that extend annotated X_* model classes.

3. Model class generator is modify to generate X_* model class with Model annotation

4. Plugin should either use method 4 above or create OSGi component that extend AnnotationBasedModelFactory class (usually with @Component(immediate = true, service = IModelFactory.class, property = "service.ranking:Integer=1") component annotation).

 @org.adempiere.base.Model(table="C_BPartner")
 public class X_C_BPartner extends PO implements I_C_BPartner, I_Persistent
 {
     ...
 }


Technical Info:
IDEMPIERE-4675, IDEMPIERE-5004, IDEMPIERE-4842

Cookies help us deliver our services. By using our services, you agree to our use of cookies.