jeudi 12 octobre 2017

Java `InvocationTargetException` with class instantiation by reflection

The problem I'm experiencing is thus: I have a series of classes that are collected via annotations. They all reside in the same folder, and if they have the particular annotation, they are instantiated via the Reflections library. While these classes are being instantiated, there is a static initializer that calls a static factory, which builds some structure. Java will throw the InvocationTargetException error while trying to obtain the factory created object. More specifically when I output the stacktrace for the ITE it points directly to the static initializer that asks the factory for the object.

Below is the code I use to replicate the issue.

I have an annotation: InferenceRule.java

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface funRule {
    String ruleName();
    String ruleType();
    String analyze() default "node";
}

I then apply that annotation to some number of classes in the package inference.rules:

@InferenceRule(ruleName = "assign", ruleType = "term")
public class Assign extends NodeAnalyzer {
    public Assign() {super();}
    public Assign(String... args) { super(args); }
    public Rule gatherAllCOnstraints(InstructionNode node) {
        // use the Identifier object here.
    }
    // rest of class here
}

The NodeAnalyzer class, the super of the Assign class above:

public abstract class NodeAnalyzer {
    protected Identifier identifier;

    protected NodeAnalyzer() {
        // Construct things here
    }

    protected NodeAnalyzer(String... args) {
        // Construct other things here
    }

    // Construct common things here
    {
        this.identifier = IdentifierFactory.getIdentifier();
    }
    // rest of class here
}

The Assign class is instantiated in the Inference class, as described below:

public class Inference {
    public final String NODE_ANALYSIS = "NODE";
    public static final String INFERENCE_PACKAGE = "inference.rules";
    private final Map<String, NodeAnalyzer> nodeAnalyzer = new HashMap<>();
    private final Map<String, EdgeAnalyzer> edgeAnalyzer = new HashMap<>();
    public Inference() {

    }
    // other non-interesting things here

    private void loadRules() {
        Reflections reflection = new Reflections(INFERENCE_PACKAGE);
        Set<Class<?>> annotated = reflection.getTypesAnnotatedWith(InferenceRule.class);

        for(Class<?> clazz : annotated) {
            try {
                String name = clazz.getAnnotation(InferenceRule.class).ruleName();
                String type = clazz.getAnnotation(InferenceRule.class).ruleType();
                String analyze = clazz.getAnnotation(InferenceRule.class).analyze();
                if (StringUtils.equalsIgnoreCase(analyze, NODE_ANALYSIS)) {
                    final NodeAnalyzer newInstance = (NodeAnalyzer) clazz.getConstructor(InferenceType.class).newInstance(InferenceType.valueOf(type));
                    this.nodeAnalyzer.put(name, newInstance);
                }
                // handle other cases...
            } catch(InvocationTargetException ite) {
                // For debugging, only
                ite.printStackTrace();
                logger.error(ite.getCause.getMessage());
                logger.error(ite.getTargetException.getMessage());
            }
        }
    }
}

As you can see, from the instantiation path in Assign and NodeAnalyzer, it must call the IdentifierFactory class:

public class IdentifierFactory {
    private static final Identifier identifier;
    static {
        if (ConfigFactory.getConfig().isDebEnabled()) {
            identifier = new DBIdentifier();
        } else {
            identifier = new NaiveIdentifier();
        }
    }

    public static Identifier getIdentifier() {
        return identifier;
    }
}

The ConfigFactory class follows a similar pattern as the IdentifierFactory class. It builds a config based on certain input.

The exact exception thrown looks like:

java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at phases.inference.Inference.loadRules(Inference.java:197)
    at phases.inference.Inference.<init>(Inference.java:76)
    at phases.PhaseFacade$PHASES.getPhase(PhaseFacade.java:27)
    at phases.PhaseFacade.<init>(PhaseFacade.java:42)
    at compilation.Compiler.runPhases(Compiler.java:126)
    at compilation.Compiler.runAllOps(Compiler.java:118)
    at Main.main(Main.java:45)
Caused by: java.lang.ExceptionInInitializerError
    at phases.inference.rules.NodeAnalyzer.<init>(NodeAnalyzer.java:35)
    at phases.inference.rules.Assign.<init>(Assign.java:22)
    ... 11 more
Caused by: java.lang.NullPointerException
    at typesystem.identification.NaiveIdentifier$1.<init>(NaiveIdentifier.java:23)
    at typesystem.identification.NaiveIdentifier.<init>(NaiveIdentifier.java:22)
    at typesystem.identification.IdentifierFactory.<clinit>(IdentifierFactory.java:25)
    ... 13 more

and:

java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at phases.inference.Inference.loadRules(Inference.java:197)
    at phases.inference.Inference.<init>(Inference.java:76)
    at phases.PhaseFacade$PHASES.getPhase(PhaseFacade.java:27)
    at phases.PhaseFacade.<init>(PhaseFacade.java:42)
    at compilation.Compiler.runPhases(Compiler.java:126)
    at compilation.Compiler.runAllOps(Compiler.java:118)
    at Main.main(Main.java:45)
Caused by: java.lang.NoClassDefFoundError: Could not initialize class typesystem.identification.IdentifierFactory
    at phases.inference.rules.NodeAnalyzer.<init>(NodeAnalyzer.java:35)
    at phases.inference.rules.Assign.<init>(Assign.java:18)
    ... 11 more

From these, I cannot adequately discern what the root cause is. To further complicate this, I have tried to run this using other input files and it works just fine on those.





Aucun commentaire:

Enregistrer un commentaire