ActiveObjects Implementation class requires constructor args

Carl Myers
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 20, 2014

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

0 votes
cofarrell
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 20, 2014

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

Carl Myers
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 20, 2014

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 ?

cofarrell
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 20, 2014

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

Carl Myers
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 21, 2014

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.

0 votes
Carl Myers
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 20, 2014

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.

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events