Reflection property injection

reflection based property injection

Introduction

Occasionally a managed node may not expose its properties or constructors making it difficult to inject managed nodes or properties. This can happen if a class uses a static factory method that invokes a private constructor.

Fluxtion supports private member assignment and synthetic default constructor usage. All scalar and vector properties are supported in earlier sections. Non-transient fields will be ignored.

Warning

The Jvm security model is evolving and it is unlikely reflection support and synthetic default constructors will be available in the future. This facility should be validated on your target runtime.

Check reflection works on your target platform, support is nor guaranteed by Fluxtion.

Usage

The use of refelction assignment has to be explicitly enabled in the Fluxtion compiler with the following option:

 -pa,--assignPrivate <arg>                If set generated SEP will
                                          attempt to use reflection style
                                          assignment for private
                                          non-transient members

With the Fluxtion maven plugin the feature must be explicitly enabled:

fluxtion:generate
  Description: A mojo to wrap the invocation of the Fluxtion executable.
  Implementation: com.fluxtion.maven.FluxtionGeneratorMojo
  Language: java
  Bound to phase: compile

  Available parameters:

    assignNonPublicMembers (Default: false)
      User property: assignNonPublicMembers
      Override whether the generated static event processor supports reflection
      based assignment for initialisation.

Example

The following example demonstrates reflection based assignment for a class with a static factory method, private constructor and no mutator methods for fields.

The code is located here.

Node class

The factory method, build acts as a gateway to the private constructor and fields.

public class FactoryNode {  
    
    private final Object parent;
    private int limit;
    private transient double transientValue;

    public FactoryNode(Object parent) {
        this.parent = parent;
    }
    
    public static final FactoryNode build(Object parent, int limit){
        FactoryNode node = new FactoryNode(parent);
        node.limit = limit;
        node.transientValue = limit * 100;
        return node;
    }

    public Object getParent() {
        return parent;
    }

    public int getLimit() {
        return limit;
    }
    
    @OnEvent
    public void onEvent(){}
    
}

SEPConfig builder

public class Builder extends SEPConfig{

    @Override
    public void buildConfig() {
        addNode(FactoryNode.build(addNode(new MyEventHandler()), 10000));
    }
    
}

Maven plugin config

The example uses maven as the build system. The configuration to enable reflection assignment is shown below. The critical element is on line 11

<assignNonPublicMembers>true</assignNonPublicMember>

Full config

    <execution>
        <id>property-refelection</id>
        <goals>
            <goal>generate</goal>
        </goals>
        <configuration>
            <configClass>com.fluxtion.example.core.dependencyinjection.reflection.Builder</configClass>
            <packageName>com.fluxtion.example.core.dependencyinjection.reflection.generated</packageName>
            <className>SampleProcessor</className>
            <outputDirectory>src/main/java</outputDirectory>
            <assignNonPublicMembers>true</assignNonPublicMembers>
        </configuration>
    </execution>

Generated SEP

Note the use of Mirror to invoke a synthetic default constructor that does not exist and reflection to mutate the fields of the created node.

public class SampleProcessor implements EventHandler, BatchHandler, Lifecycle {

  //Node declarations
  final net.vidageek.mirror.dsl.Mirror constructor = new net.vidageek.mirror.dsl.Mirror();
  private final MyEventHandler myEventHandler_1 = new MyEventHandler();
  private final FactoryNode factoryNode_3 =
      constructor.on(FactoryNode.class).invoke().constructor().bypasser();
  //Dirty flags

  //Filter constants

  public SampleProcessor() {
    final net.vidageek.mirror.dsl.Mirror assigner = new net.vidageek.mirror.dsl.Mirror();
    assigner.on(factoryNode_3).set().field("parent").withValue(myEventHandler_1);
    assigner.on(factoryNode_3).set().field("limit").withValue((int) 10000);
  }

  @Override
  public void onEvent(com.fluxtion.runtime.event.Event event) {
    switch (event.getClass().getName()) {
      case ("com.fluxtion.example.shared.MyEvent"):
        {
          MyEvent typedEvent = (MyEvent) event;
          handleEvent(typedEvent);
          break;
        }
    }
  }

  public void handleEvent(MyEvent typedEvent) {
    //Default, no filter methods
    myEventHandler_1.handleEvent(typedEvent);
    factoryNode_3.onEvent();
    //event stack unwind callbacks
    afterEvent();
  }

  @Override
  public void afterEvent() {}

  @Override
  public void init() {}

  @Override
  public void tearDown() {}

  @Override
  public void batchPause() {}

  @Override
  public void batchEnd() {}
}

Last updated