Liferay 7: OSGi Component as prototype

thumbnail
Stan M, modified 5 Years ago. New Member Posts: 8 Join Date: 5/28/15 Recent Posts
Hi, I'm trying to create multiple instances of a single OSGi-component, but have not succeeded yet. In Liferay 6, I would use spring-beans (having 'prototype'-property set) to get a separate instance for each e.g. user. In Liferay 7 I'm using OSGi and the common use case of the OSGi-components is to manage them as singletons. I've found that these Components can be Prototyped (scope= ServiceScope.PROTOTYPE), but I'm not sure that this approach is correct. Maybe I should use other way to accomplish mutliple instances (not OSGi)? Thanks!
thumbnail
Christoph Rabel, modified 5 Years ago. Liferay Legend Posts: 1555 Join Date: 9/24/09 Recent Posts
How are you creating multiple instances? What is your usecase?
What are you trying to accomplish? One instance per bundle? One instance per component (per require)? Maybe even one instance per request?
thumbnail
Stan M, modified 5 Years ago. New Member Posts: 8 Join Date: 5/28/15 Recent Posts
Hi Christoph, I need to create a separate Component instance for each loggedIn user in OSGi environment.  So I plan to use  (scope= ServiceScope.PROTOTYPE), but I'm not sure that this is the correct way. Looking forward to you reply.
thumbnail
Christoph Rabel, modified 5 Years ago. Liferay Legend Posts: 1555 Join Date: 9/24/09 Recent Posts
For each logged in user or for each request? I am not sure, why you want one instance per user. If you want to keep data in it, I'd rather use a cache mechanism or some other storage.

In any case, PROTOTYPE_REQUIRED is not sufficient, you will still, by default get only one instance per component.

@Component A {

@Reference(scope=ReferenceScope.PROTOTYPE_REQUIRED)
Something instance_for_A;
}

@Component B {

@Reference(scope=ReferenceScope.PROTOTYPE_REQUIRED)
Something instance_for_B;
}

While the instances in A and B are different, there are still only two of them. You either need to use a factory or some different approach. With PROTOTYPE you could do something like this:
@Reference(scope = ReferenceScope.PROTOTYPE_REQUIRED)
public void setFactory(ComponentServiceObjects<Something> factory) {
  this.factory = factory;
}
  Something x = factory.getService();
  ... do something with x
    factory.ungetService(x);

But I am not sure, how this helps you for your usecase. Maybe it does, I don't know.
thumbnail
Stan M, modified 5 Years ago. New Member Posts: 8 Join Date: 5/28/15 Recent Posts
Christoph, thanks a lot for your reply, I've imlemented the approach you've proposed and I do able to create  an instance for each request. That's cool. But I also need to keep that instance within user session. I put it to PortletSession and I don't think it's right.  My use case is - I have a 'calculation-tool' that should keep intermediate values and some math-operations performed by the user, I keep them now in an instance produced by the factory. This produced instance, in turn, is  stored to user session (PortletSession), so the user could always return to prev calculation step. But when a portlet session expires this instance is not removed from the memory (until the bundle is restarted).
You've also mentioned to use a cache, can you give a hint on a class that can do it?  I can also store this values to DB, but I don't want it since there are many not loggedIn users will use this calc-tool.
thumbnail
Christoph Rabel, modified 5 Years ago. Liferay Legend Posts: 1555 Join Date: 9/24/09 Recent Posts
I think, you should not store a component anywhere. While I tried to explain what you can do to you, I don't think that the approach is sound.
I would create a simple DTO (data transfer object) to hold the data. Store the data you need in there. When you need to do calculations, give them to the components as parameter. We use DTOs quite a lot to transfer data through the various layers.
This doesn't solve your storage problem, though. The main question is: Can you afford to lose that object at any time? If it contains only intermediate values and basically is used to speedup things, a cache is the best option. If losing these data is really bad, you should persist them in the database.
For cache, you can use the SingleVM or the MultiVM cache. Here is some Liferay code that uses it. If you have no cluster, use SingleVM, if you have a cluster, then it depends. SingleVM could still be fine for your usecase. You probably need no listeners, you just need to put and fetch objects.
https://github.com/liferay/liferay-portal/blob/7.2.x/portal-impl/src/com/liferay/portal/template/BaseTemplateResourceCache.java