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

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.

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 Nov 29, 2018 in Marketplace Apps

How to set up an incident workflow from the VP of Engineering at Sentry

Hey Atlassian community, I help lead engineering at Sentry, an open-source error-tracking and monitoring tool that integrates with Jira. We started using Jira Software Cloud internally last year, a...

1,447 views 0 8
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