ActiveObjects Implementation class requires constructor args

I have the following test case:

@Implementation(FooImpl.class)
public class Foo extends Entity {

    @Accessor
    public Integer getRepoId();
    @Mutator
    public void setRepoId(Integer id);

    @Ignore
    public Repository getRepository();
}

public class FooImpl {

    private final Foo foo;
    private final RepositoryService rs;

    public FooImpl(Foo foo, RepositoryService rs) {
        this.foo = foo;
        this.rs = rs;
    }
    
    public Repository getRepository() {
        return repositoryService.getById(foo.getRepoId());
    }
}

When I try to use this code, I get NPEs because, it seems, AO silently fails to construct the FooImpl object (see the code in ImplementationWrapper.java and EntityProxy.java in the activeobjects code here: https://github.com/djspiewak/activeobjects.git).

How can I write implementation code which depends upon a component spring must wire up, such as RepositoryService? ALso, is this a bug in AO that it doesn't provide a better error? The error I got looked like this:

java.lang.NullPointerException
	at org.hsqldb.jdbc.jdbcResultSet.findColumn(Unknown Source)
	at org.hsqldb.jdbc.jdbcResultSet.getObject(Unknown Source)
	at net.java.ao.EntityProxy.isNull(EntityProxy.java:1185)
	at net.java.ao.EntityProxy.convertValue(EntityProxy.java:1162)
	at net.java.ao.EntityProxy.invokeGetter(EntityProxy.java:757)
	at net.java.ao.EntityProxy.invoke(EntityProxy.java:199)
	at $Proxy22.getLCName(Unknown Source)
	at ut.com.palantir.stash.TestCaseTest.testTestCase(TestCaseTest.java:69)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
	at net.java.ao.test.junit.ActiveObjectTransactionMethodRule$1.evaluate(ActiveObjectTransactionMethodRule.java:86)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

(in this instance, TestCase is Foo and TestCaseImpl is FooImpl, and getLCName is the method implemented by the impl class)

2 answers

This widget could not be displayed.

Just to add, I am pretty sure of the cause because after a lengthy debugging session, I removed the extra arguments from the constructor and the error went away (but of course, my code doesn't work because I need repositoryService). Also, as I said, I read the AO code.

This widget could not be displayed.

Hi Carl,

I'm afraid, unless I'm mistaken, that you can't use inject Spring beans into your AO beans. These are meant to be simple data transfer objects (DTOs).

Instead you normally want to load (or create) an instance of Foo in a service/component X, which has injected (via the constructor) an instance of RepositoryService. You can then lookup the Stash repository from the id of the Foo repoId. Don't try to mix the two.

If you get stuck I might suggest putting some code on Bitbucket/Github so that we can look at and perhaps provide some pointers.

Cheers,

Charles

PS. Apologies if you've seen these before:

https://developer.atlassian.com/display/DOCS/Getting+Started+with+Active+Objects

Also the official Atlassian version of AO is kept here:

https://bitbucket.org/activeobjects

Charles - thanks for your reply. I see how to make a workaround, it's just less clean. That said, I'm still of the mind that the inability to construct an Impl class using the dependency injection framework of your choice (such as spring) is an AO bug, and I'm *sure* the way this particular error surfaces is an error. Shouldn't construction fail? Maybe an IllegalArgumentException could be thrown from the init() method of ImplementationWrapper.java ?

Hi Carl,

I'm not sure I entirely agree that you should be able to inject your impl classes with services - does Hibernate allow you to do that for example?

In any case, I agree the construction should fail with an IAE. If you want to raise a bug you might want to start here:

https://ecosystem.atlassian.net/browse/AO

Cheers,

Charles

Thanks Charles!

I don't seem to have permission to open/change issues on that jira instance (I created a new account since it seems to be disconnected from answers auth).

I found a duplicate issue resolved as "won't fix" but I think at a minimum we should reopen the issue or open a linked issue to fix the error handling. The similar issue I found is here:

https://ecosystem.atlassian.net/browse/AO-134?jql=project%20%3D%20ao%20and%20text%20~%20%22implementation%22

I would like to also open a feature request which is as follows:

Modify the code that interacts with @Implementation so if FooImpl.class implements FactoryBean interface (or something like that) then instead of treating it as the Impl class, it uses that factory bean to construct the new object. This should allow people to have custom constructors with minimal changes to the code.

Suggest an answer

Log in or Sign up to answer
Community showcase
Published Aug 22, 2018 in Marketplace Apps

How a Marketplace app tech team is achieving gender diversity

Hello! My name is Genevieve Blanch, and I'm the Marketing Manager at RefinedWiki, creators of apps to give teams the tools to customize Atlassian platforms. Currently, 44% of the tech team at Re...

522 views 3 19
Read article

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you