Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Eclipse 4 » How to @Inject a service into a called object
How to @Inject a service into a called object [message #987727] Tue, 27 November 2012 13:50 Go to next message
Jeff Rickard is currently offline Jeff Rickard
Messages: 4
Registered: November 2012
Location: Munich, Germany
Junior Member
This seems like a basic question, but I can't find an answer and I guess it's because I'm missing some basic understanding... But I can't be the only one.

I'm constructing and calling an object from a part. The part has a service successfully injected, but I can't inject into the called object. To explain with code:

public class MyPart {

    @PostConstruct
    public void postConstruct(Composite parent, IMyService myService) {
        MyClass myObject = new MyClass();
        myObject.test();
        System.out.println("*myService = " + myService);
    }
}

public class MyClass {

    @Inject IMyService myService;
    @Inject IEclipseContext eclipseContext;

    MyClass() {
    }

    void test() {
        System.out.println("myService = " + myService);
        System.out.println("eclipseContext = " + eclipseContext);
    }
}

The console output:
myService = null
eclipseContext = null
*myService = com.example.services.MyService@190d8e1


How do I inject or get access to an IMyService from MyClass?

Of course the service could be passed to myObject, but this does not work in my case (for reasons outside of scope of this question). I though the answer might be to use getBundleContext(), but although I can get the Bundle, getBundleContext() returns null.
Re: How to @Inject a service into a called object [message #987749 is a reply to message #987727] Tue, 27 November 2012 16:50 Go to previous messageGo to next message
Lars Vogel is currently offline Lars Vogel
Messages: 1049
Registered: July 2009
Senior Member

Have a look in the following parts of the Eclipse 4 RCP tutorial. They explain how to use DI for own objects: Extending the IEclipseContext and Using DI for own objects.
Re: How to @Inject a service into a called object [message #987837 is a reply to message #987749] Wed, 28 November 2012 05:55 Go to previous messageGo to next message
Jeff Rickard is currently offline Jeff Rickard
Messages: 4
Registered: November 2012
Location: Munich, Germany
Junior Member
Thanks, Lars.

I had (of course Razz ) read through this material (and other stuff) a couple of times before posting, but could not see what I was missing. I see now that the section on RunAndTrack is what I was after.

(I was thrown off the scent by the phrases "A RunAndTrack is basically a Runnable..." and "The runAndTrack() method allows a client to keep some external state synchronized...", neither of which I thought were applicable to my problem.)

My problem was that MyClass needed access to the IEclipseContext, and IEclipseContext.runAndTrack() provides a way of getting this. For completeness, here is my sample code, modified to work:

public class MyPart {

    @PostConstruct
    public void postConstruct(Composite parent, IEclipseContext context, IMyService myService) {
        MyClass myObject = new MyClass();
        context.runAndTrack(myObject);
        myObject.test();
        System.out.println("*myService = " + myService);
    }
}

public class MyClass extends RunAndTrack {

    IMyService myService;

    @Override
    public boolean changed(IEclipseContext context) {
        myService = context.getActive(IMyService.class);
        return false; // don't call on updates
    }

    void test() {
        System.out.println("myService = " + myService);
    }
}

The console output:
myService = com.example.services.MyService@190d8e1
*myService = com.example.services.MyService@190d8e1
Re: How to @Inject a service into a called object [message #987860 is a reply to message #987837] Wed, 28 November 2012 07:23 Go to previous messageGo to next message
Thomas Schindl is currently offline Thomas Schindl
Messages: 5296
Registered: July 2009
Senior Member
No, no, no. Your error is that you are creating the instance of the bean
your own instead of useing ContextInjectionFactory.make()

If you want to use DI don't do this:

MyClass myObject = new MyClass();

but do this:

MyClass myObject = ContextInjectionFactory.make(MyClass.class,context);

You don't need RunAndTrack!

Tom

Am 28.11.12 11:55, schrieb Jeff Rickard:
> Thanks, Lars.
>
> I had (of course :p ) read through this material (and other stuff) a
> couple of times before posting, but could not see what I was missing. I
> see now that the section on RunAndTrack is what I was after.
>
> (I was thrown off the scent by the phrases "A RunAndTrack is basically a
> Runnable..." and "The runAndTrack() method allows a client to keep some
> external state synchronized...", neither of which I thought were
> applicable to my problem.)
>
> My problem was that MyClass needed access to the IEclipseContext, and
> IEclipseContext.runAndTrack() provides a way of getting this. For
> completeness, here is my sample code, modified to work:
>
>
> public class MyPart {
>
> @PostConstruct
> public void postConstruct(Composite parent, IEclipseContext context,
> IMyService myService) {
> MyClass myObject = new MyClass();
> context.runAndTrack(myObject);
> myObject.test();
> System.out.println("*myService = " + myService);
> }
> }
>
>
> public class MyClass extends RunAndTrack {
>
> IMyService myService;
>
> @Override
> public boolean changed(IEclipseContext context) {
> myService = context.getActive(IMyService.class);
> return false; // don't call on updates
> }
>
> void test() {
> System.out.println("myService = " + myService);
> }
> }
>
> The console output:
>
> myService = com.example.services.MyService@190d8e1
> *myService = com.example.services.MyService@190d8e1
>
Re: How to @Inject a service into a called object [message #987862 is a reply to message #987860] Wed, 28 November 2012 07:37 Go to previous messageGo to next message
Dirk Fauth is currently offline Dirk Fauth
Messages: 1263
Registered: July 2012
Senior Member
Hi Tom,

just to be sure I understand CIF now more clearly, MyClass has to be annotated with @Creatable so CIF.make() works. Is that correct?

Greez,
Dirk
Re: How to @Inject a service into a called object [message #987867 is a reply to message #987862] Wed, 28 November 2012 07:46 Go to previous messageGo to next message
Thomas Schindl is currently offline Thomas Schindl
Messages: 5296
Registered: July 2009
Senior Member
No. @Creatable has the effect that an instance is created if needed by
another bean.

Say you have BeanA and BeanB.

BeanA {
@Inject
BeanB b;
}

BeanB {
@Inject
public BeanB(MyService service) {
}
}

If you now call:

CIF#make(BeanA,context) will normally croak and tell your that there's
no BeanB registered in context, so you'd be forced to do this.

context.set(BeanB.class, CIF#make(BeanB,context) );
BeanA a = CIF#make(BeanA,context)

If you add the @Creatable the DI container will itself create an
instance of BeanB if none is found.

Tom


Am 28.11.12 13:37, schrieb Dirk Fauth:
> Hi Tom,
>
> just to be sure I understand CIF now more clearly, MyClass has to be
> annotated with @Creatable so CIF.make() works. Is that correct?
>
> Greez,
> Dirk
Re: How to @Inject a service into a called object [message #987870 is a reply to message #987867] Wed, 28 November 2012 08:03 Go to previous messageGo to next message
Dirk Fauth is currently offline Dirk Fauth
Messages: 1263
Registered: July 2012
Senior Member
Think I got it.

So Jeff could either create a new instance of MyClass himself with CIF.make() as you described, or annotate MyClass with @Creatable and simply let the container create and inject it to his service.

Using @Creatable could have the disadvantage that a new instance is created everytime there is one requested for a context that doesn't know one (which could be avoided using @Singleton if it is desired that way). If the object is added with CIF.make() the container will not create a new instance immediately but search for an instance in the hierarchy (failing if there is none).

Please correct me if there is still something wrong in my understanding. Smile

Greez,
Dirk
Re: How to @Inject a service into a called object [message #988270 is a reply to message #987870] Thu, 29 November 2012 04:09 Go to previous messageGo to next message
Jeff Rickard is currently offline Jeff Rickard
Messages: 4
Registered: November 2012
Location: Munich, Germany
Junior Member
Hi Tom,

ContextInjectionFactory.make() does not seem like the right solution in my particular case for two reasons:

- In my real code, MyClass has a private constructor which is wrapped in a getInstance() which cannot be passed to ContextInjectionFactory.make().

- In my real code, the MyClass constructor takes a bunch of parameters which are not appropriate to first put into the context, as the constructor is called with various parameters from various places. But even if this was done, using ContextInjectionFactory.make() would involve more complicated code than using RunAndTrack.

So I think RunAndTrack is better in my real code. If you disagree with my reasoning please say so.

(Another solution would be for MyClass objects to be created by a service. This may be a better solution overall. If I have time today I'll implement that and see if it feels better.)

@Lars: The bit in your tutorial about ContextInjectionFactory.make() in section "24.1. Accessing the context" I think needs clarification. Maybe instead of starting with the phrase "If you are outside of a model object..." it would be clearer if it started with something like "If a manually created object needs access to the context...", and then explicit mention that if you use new the created class will not be able to access the context. Depending upon Tom's response to this post, mention of RunAndTrack in that section may also be useful.

[Updated on: Thu, 29 November 2012 04:12]

Report message to a moderator

Re: How to @Inject a service into a called object [message #988273 is a reply to message #988270] Thu, 29 November 2012 04:16 Go to previous messageGo to next message
Lars Vogel is currently offline Lars Vogel
Messages: 1049
Registered: July 2009
Senior Member

@Jeff: Thanks for the comments, I have a look later and update them.
Re: How to @Inject a service into a called object [message #988275 is a reply to message #988270] Thu, 29 November 2012 04:18 Go to previous messageGo to next message
Dirk Fauth is currently offline Dirk Fauth
Messages: 1263
Registered: July 2012
Senior Member
Hi,

the cases you describe are the reason for ContextInjectionFactory.inject() IMHO. You could create your own object via getInstance() and then do the injection afterwards via CIF.inject(). I still don't see the need for using RunAndTrack here.

Greez,
Dirk
Re: How to @Inject a service into a called object [message #988282 is a reply to message #988275] Thu, 29 November 2012 04:27 Go to previous messageGo to next message
Thomas Schindl is currently offline Thomas Schindl
Messages: 5296
Registered: July 2009
Senior Member
Correct - run and track is not needed for this, anyways I think your
getInstance should use CIF#make you should pass in the IEclipseContext,
create a child-context and create the instance.

Anyways CIF.inject will work - I would simply do it differently as
described above.

Tom

Am 29.11.12 10:18, schrieb Dirk Fauth:
> Hi,
>
> the cases you describe are the reason for
> ContextInjectionFactory.inject() IMHO. You could create your own object
> via getInstance() and then do the injection afterwards via CIF.inject().
> I still don't see the need for using RunAndTrack here.
>
> Greez,
> Dirk
Re: How to @Inject a service into a called object [message #988291 is a reply to message #988275] Thu, 29 November 2012 04:54 Go to previous message
Jeff Rickard is currently offline Jeff Rickard
Messages: 4
Registered: November 2012
Location: Munich, Germany
Junior Member
Hi Dirk, Tom,

Dirk is right, using ContextInjectionFactory.inject() is a syntactically cleaner solution than RunAndTrack. (From caller it makes no difference, but it makes it more obvious in the MyClass which fields are injected.) This seems to be the right solution, at least in my case. @Lars might want to mention .inject() in the tutorial.

I think that in my case Tom's solution is more complicated than I need. In other cases it may be the best solution.

(Now I understand the solution, I see that much of this topic was already covered by http://www.eclipse.org/forums/index.php/m/982712/ - my previous searching did not find that - and that topic may contain some extra information for the next person to read this post.)


For completeness for the next person to google this question, here is the solution I have. This uses ContextInjectionFactory.inject() as an alternative to ContextInjectionFactory.make() for the case where the constructor has a parameter.
public class MyPart {

    @PostConstruct
    public void postConstruct(Composite parent, IEclipseContext context) {
        MyClass myObject = new MyClass(123);
        ContextInjectionFactory.inject(myObject, context);
    }
}

public class MyClass {

    @Inject IMyService myService;

    MyClass(int param) {
    }
}

[Updated on: Thu, 29 November 2012 04:54]

Report message to a moderator

Previous Topic:Re: dynamically add part to partStack
Next Topic:Welcome page in e4
Goto Forum:
  


Current Time: Sun Aug 31 00:39:20 EDT 2014

Powered by FUDForum. Page generated in 0.02078 seconds