Ask Questions and Find Answers
Important:
Ask is now read-only. You can review any existing questions and answers, but not add anything new.
But - don't panic! While ask is no more, we've replaced it with discuss - the new Liferay Discussion Forum! Read more here here or just visit the site here:
discuss.liferay.com
RE: BundleActivator implementation and OSGi injection
Hi All,
I want to execute a piece of code every time a module is deployed! So I searched forums and I got following 2 useful APIs
com.liferay.portal.kernel.events.SimpleAction --> This gets activated ONLY while Liferay server is starting (here)
org.osgi.framework.BundleActivator --> This gets activated every time the module is deployed, so this fits (here)
If I put the LOG statements in the BundleActivator implementation, it gets printed!
But my primary purpose is to invoke a method of some other Component class from this start method.
@Component(immediate = true, service = StartupActivator.class) public class StartupActivator implements BundleActivator { @Reference private LiferayInstanceService liferayInstanceService; private static final Log LOG = LogFactoryUtil.getLog(StartupActivator.class); @Override public void start(BundleContext context) throws Exception { LOG.info("I am running NOW"); this.liferayInstanceService.setSiteCustomField("RefreshJson", Boolean.TRUE); }
Above code in start() method basically sets the "RefreshJson" site custom field to TRUE everytime this module is deployed! But I am not getting instance of liferayInstanceService injected into the class (this injection works fine in all other component classes throughout the project)! This class definition is as below...
@Component(immediate = true, service = LiferayInstanceService.class) public class LiferayInstanceService {
So I guess BundleActivator class is invoked before OSGi initializes everything else or may be something else... Is there any way to get this instance of the class successfully injected into this? Please let me know this or any other way to do so...
Thanks,
Ketan
Not sure if this will help you, but adding this reference here tells your module to wait, till the portal is initialized.
@Reference(target = ModuleServiceLifecycle.PORTAL_INITIALIZED,
unbind = "-")
protected void
setModuleServiceLifecycle(ModuleServiceLifecycle
moduleServiceLifecycle) {
}
Please note, that there is something fishy here, I don't think that this will resolve the root problem. Are you sure that you don't get a reference to LiferayInstanceService?
Ketan Solanki:But my primary purpose is to invoke a method of some other Component class from this start method.@Component(immediate = true, service = StartupActivator.class) public class StartupActivator implements BundleActivator { ... }
I might be wrong here, but a DS as a Bundle Activator sounds weird to me: A BundleActivator is run before the DS initialization is done, thus I'd expect you'll have to decide for one: There are DS-Activators and BundleActivators.
And as they're the source of resource- and memory leaks: Make sure that you deactivate if you're doing anything else than storing a String. Otherwise you might hold on to a service that's long gone and prevent its garbage collection.
Thanks very much Christoph and Olaf for your time and responses.
@Christoph: I have tried the method you suggested, it gets invoked while portlet is starting up but even in that method the liferayInstanceService remains NULL. And that's true for the start method as well.
@Olaf: I agree with you that DS and Bundle Activator don't go together! DS doesn't work at all on BundleActivator!
So I tried something completely different, like wrote the code of getting the site, and setting the field from the class itself - as below:
private void setRefreshJsonTrue() { try { String siteName = ApplicationConfig.getInstance().defaultSiteName(); List<Group> groups = GroupLocalServiceUtil.getGroups(0, 10000); for (Group group : groups) { if (group.getDescriptiveName().equalsIgnoreCase(siteName)) { group.getExpandoBridge().setAttribute("RefreshJson", Boolean.TRUE, true); } } } catch (PortalException pe) { LOG.error("Error occurred while getting the site, try again!"); } }
I invoked above method from the start() but I got different and, obviously, legitimate exception.
Caused by: java.lang.RuntimeException:
com.liferay.portal.kernel.security.auth.PrincipalException:
PermissionChecker not initialized
at com.liferay.portlet.expando.model.impl.ExpandoBridgeImpl.setAttribute(ExpandoBridgeImpl.java:444)
Because DS runs after bundle activator...
I am sure someone would have definitely faced an issue like this, where a site custom field is changed everytime a deployment is made!
Please guide me how to resolve this issue.
Thanks,
Hi,
what you needed was org.osgi.framework.BundleListener
,
not BundleActivator. BundleListener receives events about other
bundles, BundleActivator fires when your module starts. Or better
use org.osgi.util.tracker.BundleTracker
that is easier
and simpler to use.
Related to the PermissionChecker exception: do you need to run the code with some signed in user privileges? It looks rather like a system job or similar, correct? In such case you can call setAttribute(..., false), i.e. something like this:
group.getExpandoBridge().setAttribute("RefreshJson", Boolean.TRUE, false);
Another problem is that OSGi
modules usually start before portal is initialized, that means portal
Spring services (including Group or Expando services) might not be up.
It's a good practice to reference directly GroupLocalService from the
component and don't rely on GroupLocalServiceUtil (static class) to
make sure portal is up. Otherwise you need to reference this
"service" to make sure your component activates after portal
is up: @Reference(target =
ModuleServiceLifecycle.PORTAL_INITIALIZED, unbind = "-")
So please use this:
@Reference private GroupLocalService groupLocalService; private void setRefreshJsonTrue() { ... List<Group> groups = groupLocalService.getGroups(0, 10000); ... }
HTH.
Thanks Tomas for your time and response. This statement
group.getExpandoBridge().setAttribute("RefreshJson", Boolean.TRUE, false);
Has done the magic, along with BundleActivator :) so basically when I passed secure: true as the last parameter, Liferay was checking all the permission logic and permission checker wasn't yet initialized so it threw exception!
I tried with BundleTracker but somehow it's overridden methods were not printing the logs while deploying the portal.
Anyways, problem solved - and once again thanks very much for the solution...
Powered by Liferay™