RE: Calling LocalService in a non controller class

Iñigo Boyano, modified 6 Years ago. Junior Member Posts: 96 Join Date: 2/4/14 Recent Posts
Hi, 

When I try to call a service (generated by serviceBuilder) from a class that is not a @Component, when the call is executed, I give the followin error

"EndpointRegistration[{"patterns":["/portlet-servlet/*"], "name":"com.lifer...etServlet", "servletInfo":"", "asyncSupported":false, "initParams":{}, "servletContextId":7594, "serviceId":7602}]"
"WebappClassLoader
  context: ROOT
  delegate: false
----------> Parent Classloader:
java.net.URLClassLoader@2d6e8792
"

The same call executed in a @Component class works properly.

I have the following sentences:

private SubTipoSubscripcionLocalService subTipoSubscripcionLocalService;

@Reference(unbind = "-")
public void setSubTipoSubscripcionLocalService(SubTipoSubscripcionLocalService subTipoSubscripcionLocalService) {
    this.subTipoSubscripcionLocalService = subTipoSubscripcionLocalService;
}
any idea?
thumbnail
Christoph Rabel, modified 6 Years ago. Liferay Legend Posts: 1555 Join Date: 9/24/09 Recent Posts
Probably the service is simply null.
Add some log output to setSubTipoSubscripcionLocalService, I guess it is never set.

Could you check, if that is the case? If yes, you could try to use the Util class.
thumbnail
Olaf Kock, modified 6 Years ago. Liferay Legend Posts: 6441 Join Date: 9/23/08 Recent Posts
Iñigo BoyanoHi, 

When I try to call a service (generated by serviceBuilder) from a class that is not a @Component, when the call is executed, I give the followin error
I'm assuming that you're obtaining the service through a @Reference annotation? @Reference is only resolved in @Component classes, not outside. So you'll need to find another method to pass the service reference to the methods in question explicitly.
Iñigo Boyano, modified 6 Years ago. Junior Member Posts: 96 Join Date: 2/4/14 Recent Posts
Hi Olaf, 

If I make the call in a *LocalServiceUtils instead of LocalService, the method works properly but, searching about good practices, i fount that is not properly to use the classes *LocalServiceUtils to call services outside @Component class in Liferay 7.0. (https://community.liferay.com/es/forums/-/message_boards/message/83433590#_com_liferay_message_boards_web_portlet_MBPortlet_message_83437418)

I think is not properly either to make a Utils class a @Component, right? So, how is the best way to access services methods in a Utils Class?

Thanks Olaf, 

​​​​​​​Kind Regards, 
thumbnail
Olaf Kock, modified 6 Years ago. Liferay Legend Posts: 6441 Join Date: 9/23/08 Recent Posts
Iñigo Boyano

If I make the call in a *LocalServiceUtils instead of LocalService, the method works properly but, searching about good practices, i fount that is not properly to use the classes *LocalServiceUtils to call services outside @Component class in Liferay 7.0. (https://community.liferay.com/forums/-/message_boards/message/83433590)

I think is not properly either to make a Utils class a @Component, right? So, how is the best way to access services methods in a Utils Class?
If you talk about Liferay's service-Util classes: They have all static methods anyways, so there's no value in making them a @Component.

My recommendation would be to either add the service as a parameter, or initialize (and uninitialize) them from any other @Component's initialization, e.g. @Activate method or in the @Reference's setter. Make sure that you also uninitialize the objects when the service is signalled to go away. You simply shouldn't use servicebuilder's *ServiceUtil classes, because they effectively hide your dependency on the actual service.
thumbnail
David H Nebinger, modified 6 Years ago. Liferay Legend Posts: 14933 Join Date: 9/2/06 Recent Posts
Iñigo Boyano
If I make the call in a *LocalServiceUtils instead of LocalService, the method works properly but, searching about good practices, i fount that is not properly to use the classes *LocalServiceUtils to call services outside @Component class in Liferay 7.0.  
In general, using OSGi methods to access OSGi components is a best practice.

For your own @Component classes, this means @Reference'ing the *LocalService and have OSGi just inject it for you.

For non @Component classes, this can mean using a ServiceTracker to get an instance when you need it (you don't hold the instance, you only hold the tracker and get the *LocalService from the tracker to complete a call).

The *LocalServiceUtil classes are holdovers from 6.x and earlier days. They suffice for helping to upgrade legacy code while minimizing change.

They have one big problem though - they throw NPEs when invoked at the wrong time because they actually do not know about the availability of the service to invoke.

For example, I can deploy an API module with a service, FailLocalService and its corresponding FailLocalServiceUtil. Now lets say I do not deploy the service module or, even worse, I deployed it but it has some kind of issue and it didn't start and I just haven't seen the error yet. Either way, my implementation of FailLocalService is not going to be available.

I can deploy code that uses an @Reference or a ServiceTracker to get the FailLocalService, in each case I will know about the lack of an implementation. The @Component just won't start and the ServiceTracker will know that there is no available service, so as the developer I have visibility on the fact the service is not available to use.

But code I write that uses FailLocalServiceUtil? That code will start and I will have no idea the service is really not available. Until, of course, my code invokes a FailLocalServiceUtil method and I get a mysterious NullPointerException and a stack trace pointing at the FailLocalServiceUtil.

That is why I recommend not using the *LocalServiceUtil classes in most cases. For upgrades, sure, because you want to minimize time, cost and impact when getting the upgrade done. But for any new development, it is worth getting the OSGi lookups in there and getting them right because it prevents a runtime failure due to a missing service instance.
​​​​​​​
Iñigo Boyano, modified 6 Years ago. Junior Member Posts: 96 Join Date: 2/4/14 Recent Posts
Hi david, 

First to answer to Olaf, declaring the services in a @Component class and passing them as params to my Utils class methods works properly.

However when i try to implement a getter in my own entity to get another entity, the easiest way to do it is by using *LocalServiceUtil like Liferay would use it in its own code.
E.g. You have the UserImpl Class to get the Contact entity. To develop it i have seen that you use the next sentence in your UserImpl.java
@Override
public Contact fetchContact() {
   if (_contact == _NULL_CONTACT) {
      return null;
   }

   if (_contact == null) {
      Contact contact = ContactLocalServiceUtil.fetchContact(
         getContactId());

      if (contact == null) {
         _contact = _NULL_CONTACT;
      }
      else {
         _contact = contact;
      }
   }

   return _contact;
}
Is there any way to develop this without using *LocalServiceUtil or it's correct to call LocalServiceUtils in a Model Impletementation Layer like you have in your kernel?

Kind regards, 

Íñigo.
thumbnail
Olaf Kock, modified 6 Years ago. Liferay Legend Posts: 6441 Join Date: 9/23/08 Recent Posts
Iñigo Boyano
Is [...] it[...] correct to call LocalServiceUtils in a Model Impletementation Layer like you have in your kernel?
No. The kernel can get away with it, as those are most likely kernel provided services. And when the kernel is there, the services will be there. But as soon as they're migrated out, they might run into the problems that David describes above.
Even if you find *LocalServiceUtil calls outside of the kernel, in modules: I'd call that a bug. Maybe a potential bug.
thumbnail
David H Nebinger, modified 6 Years ago. Liferay Legend Posts: 14933 Join Date: 9/2/06 Recent Posts
Is there any way to develop this without using *LocalServiceUtil or it's correct to call LocalServiceUtils in a Model Impletementation Layer like you have in your kernel?
No, that's like the one place that I have not been able to find a way around avoiding the *LocalServiceUtil classes.

The *ModelImpl's are instantiated internally by SB and Hibernate, so there's no entry point to inject services.

I've tried overriding and injecting at the *LocalServiceImpl or in a ServiceWrapper as that's a way to get an external service wired in and provide it to the model, but that breaks down if/when you are pulling a list in bulk.