How to include external module JARs

Kevin Neibarger, modified 6 Years ago. Regular Member Posts: 105 Join Date: 2/2/18 Recent Posts
I have a web project in which I want to include a separate osgi module that connects to a different database. I essentially want the same application to be able to connect to multiple databases. I tried the below blog but it appears to be using Maven dependencies

https://community.liferay.com/es/blogs/-/blogs/osgi-module-dependencies

From the blog:

https://community.liferay.com/es/blogs/-/blogs/liferay-7-service-builder-and-external-databases

There is the bullet point
Only one data source can be used in a single ServiceBuilder module.  If you have three different external data sources, you must create three different ServiceBuilder modules, one for each data source.
How do we do this?? I can create these separate modules, compile and deploy them, but I need to be able to reference them from an external osgi web module and I'm having compile problems.

Is there a blog or tutorial on how to build a portlet with many different external datasources/service builder modules? That would be really helpful
thumbnail
David H Nebinger, modified 6 Years ago. Liferay Legend Posts: 14933 Join Date: 9/2/06 Recent Posts
No, but there's no magic to it...

You'd have 3 separate SB services, so 3 api modules and 3 service modules.  Each service would be built around accessing the tables/data in one (and only one) of the external sources.

From the web perspective, you'd need to use compileOnly on the 3 api modules; at this point the web module is just invoking 3 sets of interfaces, it doesn't know that they are external databases or anything like that.

Compile/runtime reference issues can crop up, but often times these are just a matter of getting all of the project details squared away. If you provide a little more context about the errors you're seeing, we can help you straighten them out.
Kevin Neibarger, modified 6 Years ago. Regular Member Posts: 105 Join Date: 2/2/18 Recent Posts
So, are you talking compileOnly in the build.gradle file for that web module? Because my configuration seems to keep failing and telling me that the project path could not be found. And using the syntax for group and name goes and tries to get the dependency from Maven. 

Project with path ':TestServiceBuilder-ExtDB-api' could not be found in project ':TestServiceBuilder-web'.

My gradle buildfile snippet

dependencies {

compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel", version: "3.0.0"

compileOnly group: "com.liferay.portal", name: "com.liferay.util.taglib", version: "2.6.0"

compileOnly group: "com.liferay.portal", name: "com.liferay.util.java", version: "3.0.0"

compileOnly group: "com.liferay", name: "com.liferay.portal.spring.extender", version: "2.0.15"

compileOnly group: "javax.portlet", name: "portlet-api", version: "2.0"

compileOnly group: "javax.servlet", name: "javax.servlet-api", version: "3.0.1"

compileOnly group: "jstl", name: "jstl", version: "1.2"

compileOnly group: "org.osgi", name: "osgi.cmpn", version: "6.0.0"

compileOnly group: "org.osgi", name: "org.osgi.util.tracker", version: "1.5.2"

compileOnly group: "org.osgi", name: "org.osgi.framework", version: "1.8.0"

//compileOnly group: "com.test.db1", name: "com.test.db1.service.builder.api", version: "1.0.0"

compileOnly project(':TestServiceBuilder-api')

compileOnly project(':TestServiceBuilder-service')

[b]compileOnly project(':TestServiceBuilder-ExtDB-api')[/b]

}

I am using Liferay IDE and have a Java Project with 3 sub projects, TestServiceBuilder-service, TestServiceBuilder-api, TestServiceBuilder-web. This internal service builder connects to the Liferay lportal DB.

I have an external project called TestServiceBuilder-ExtDB with the api and service modules. That service builder module connects to my external DB. What I want to is configure that external module so that I can call the following code in a Java class from the TestServiceBuilder-web project:


@Reference
private static SystemAccessRequestsLocalService _sysAccessReqService;

I need to know how to configure my gradle buildfile(s) to allow TestServiceBuilder-web to use classes from the external module TestServiceBuilder-ExtDB. Once I understand that configuration I can figure out all of the compile errors I get.
thumbnail
David H Nebinger, modified 6 Years ago. Liferay Legend Posts: 14933 Join Date: 9/2/06 Recent Posts
The Gradle project paths should be relative from the root path.

By default, the SB projects are in a subdir off of modules, so the default would be:
​​​​​​​
compileOnly project(':modules:TestServiceBuilder:TestServiceBuilder-api')

Note that the norm is to not include the -service module in other modules as this bleeds your dependencies around. It's the norm, but not always followed; if you are going to include it, just understand why.
Kevin Neibarger, modified 6 Years ago. Regular Member Posts: 105 Join Date: 2/2/18 Recent Posts
Thanks, I did figure out that the service module projects had to be relative from the root path. I did notice another issue, the DB table I'm connecting to have 400 rows of the data, yet the following is returning 0 results with no exceptions

Class where the local service class is being referenced

@Reference

private static SystemAccessRequestsLocalService _sysAccessReqService;

public static String getAllFooStuff() {


try {


_sysAccessReqService =

SystemAccessRequestsLocalServiceUtil.getService();


List<systemaccessrequests> getAllSysAccessReq = _sysAccessReqService.getAllSystemAccessRequests();

System.out.println("\n -- Count of SystemAccess Requests(1): " + getAllSysAccessReq.size() + " --\n");</systemaccessrequests>

Local Service Implementation class (SystemAccessRequestsLocalServiceImpl.java)

public class SystemAccessRequestsLocalServiceImpl

extends SystemAccessRequestsLocalServiceBaseImpl {

/*

* NOTE FOR DEVELOPERS:

*

* Never reference this class directly. Always use {@link com.test.db1.service.builder.service.SystemAccessRequestsLocalServiceUtil} to access the system access requests local service.

*/


public List<systemaccessrequests> getAllSystemAccessRequests() {


List<systemaccessrequests> list = SystemAccessRequestsUtil.findAll();

System.out.println("\n -- Got list from Local Service: " + list.size() + " -- \n");


return SystemAccessRequestsUtil.findAll();

}

}</systemaccessrequests></systemaccessrequests>

My two System.out.println's look like


 -- Got list from Local Service: 0 -- 
 -- Count of SystemAccess Requests(1): 0 --
I've verified that there is data in the database. I would think that if the persistence layer failed to return anything I'd see exceptions of some sort.. So, I'm pretty confident that I'm connect to the database via the persistence layer, it's just not returning results.

Do I need to use the @ServiceReference annotation somewhere?
thumbnail
David H Nebinger, modified 6 Years ago. Liferay Legend Posts: 14933 Join Date: 9/2/06 Recent Posts
1. Never use XxxLocalServiceUtil.getService() as that is not the right context for you.  Instead, use the @Reference OSGi annotation to bind in the service instance.

2. Inside of XxxLocalServiceImpl, never use any of the XxxLocalServiceUtil classes, there is no reason to jump through those hoops.  ServiceBuilder injects and binds in necessary referenced services, just take a look for _xxxLocalService as super member fields.

Now, as far as results, it should be executing against the data source; you can enable the SQL display using the portal-ext.property key hibernate.show_sql=true to see what it is generating. You can then verify manually what it is running and what results it is getting back.

Note that you may not actually be pointing at the table you think you are pointing at. Check your Liferay database for NAMESPACE_Xxx where NAMESPACE is the value you used in service.xml along with the table name. If you have a table there, it could be that you are selecting records from that table instead of your external datasource.
​​​​​​​
No, you don't need to use @ServiceReference anywhere. That is just used by folks customizing the SB code, and 9 times out of 10 that will not anyone in the forums.
Kevin Neibarger, modified 6 Years ago. Regular Member Posts: 105 Join Date: 2/2/18 Recent Posts
David H Nebinger1. Never use XxxLocalServiceUtil.getService() as that is not the right context for you.  Instead, use the @Reference OSGi annotation to bind in the service instance.

2. Inside of XxxLocalServiceImpl, never use any of the XxxLocalServiceUtil classes, there is no reason to jump through those hoops.  ServiceBuilder injects and binds in necessary referenced services, just take a look for _xxxLocalService as super member fields.

Now, as far as results, it should be executing against the data source; you can enable the SQL display using the portal-ext.property key hibernate.show_sql=true to see what it is generating. You can then verify manually what it is running and what results it is getting back.

Note that you may not actually be pointing at the table you think you are pointing at. Check your Liferay database for NAMESPACE_Xxx where NAMESPACE is the value you used in service.xml along with the table name. If you have a table there, it could be that you are selecting records from that table instead of your external datasource.
​​​​​​​
No, you don't need to use @ServiceReference anywhere. That is just used by folks customizing the SB code, and 9 times out of 10 that will not anyone in the forums.
For #1, what would I use instead of XxxLocalServiceUtil.getService()? How would I bind the instance using OSGI annotation? Is there an example of this?
For #2, where are these super member fields? In the extended class, in the implemented interface?

If these *Util classes are never to be used, then why do they still exist?
thumbnail
David H Nebinger, modified 6 Years ago. Liferay Legend Posts: 14933 Join Date: 9/2/06 Recent Posts
1. You have an example in your first reply:

@Reference
private SystemAccessRequestsLocalService _sysAccessReqService;

2. The fields are defined in the base class for the service. They use the @ServiceReference because they are wired in by the Spring context, but that's nothing you need to worry about. Just trust that they are there and wired in for your use. You can find them using the autocomplete feature of your IDE when you are writing XxxLocalServiceImpl code.

The XxxLocalServiceUtil classes serve two simple purposes: 1) legacy code will still use the Util classes to simplify legacy upgrades and 2) sometimes it is advantagious for JSPs when you don't have control over to java code (in order to pass the injected service reference).

​​​​​​​But they are absolutely legacy and should be avoided unless absolutely necessary.   
Kevin Neibarger, modified 6 Years ago. Regular Member Posts: 105 Join Date: 2/2/18 Recent Posts
David H Nebinger1. You have an example in your first reply:

@Reference
private SystemAccessRequestsLocalService _sysAccessReqService;

2. The fields are defined in the base class for the service. They use the @ServiceReference because they are wired in by the Spring context, but that's nothing you need to worry about. Just trust that they are there and wired in for your use. You can find them using the autocomplete feature of your IDE when you are writing XxxLocalServiceImpl code.

The XxxLocalServiceUtil classes serve two simple purposes: 1) legacy code will still use the Util classes to simplify legacy upgrades and 2) sometimes it is advantagious for JSPs when you don't have control over to java code (in order to pass the injected service reference).

​​​​​​​But they are absolutely legacy and should be avoided unless absolutely necessary.   
If I just use the following

@Reference private SystemAccessRequestsLocalService _sysAccessReqService;

The variable _sysAccessReqService is always null. If it's wired in, it should not be null I would imagine. So, obviously calls on that variable throw a NullpointerException. Is there something else that needs to be configured?
thumbnail
Olaf Kock, modified 6 Years ago. Liferay Legend Posts: 6441 Join Date: 9/23/08 Recent Posts
Kevin Neibarger
If I just use the following

@Reference private SystemAccessRequestsLocalService _sysAccessReqService;

The variable _sysAccessReqService is always null. If it's wired in, it should not be null I would imagine. So, obviously calls on that variable throw a NullpointerException. Is there something else that needs to be configured?
@Reference only works in @Component classes. Is the class that you're injecting the reference to a declarative service (aka component)?
Kevin Neibarger, modified 6 Years ago. Regular Member Posts: 105 Join Date: 2/2/18 Recent Posts
Olaf Kock
Kevin Neibarger
If I just use the following

@Reference private SystemAccessRequestsLocalService _sysAccessReqService;

The variable _sysAccessReqService is always null. If it's wired in, it should not be null I would imagine. So, obviously calls on that variable throw a NullpointerException. Is there something else that needs to be configured?
@Reference only works in @Component classes. Is the class that you're injecting the reference to a declarative service (aka component)?

Olaf, I'm not using @Reference in a @Component class. This is the reason why I have to use the following code so the variable from the @Reference is not null

_sysAccessReqService = XxxLocalServiceUtil.getService();

The above line is not supposed to be used, but it's the only thing that works with how my code is currently setup. I'm converting code from Liferay 6.2 to 7.1, so there is alot of use of the xXXUtil classes. I'd prefer not to have to re-write the whole code base, hence the reason I'm calling getService()

Is there another/better way we can use @Reference in a POJO that is not in a @Component class?
thumbnail
David H Nebinger, modified 6 Years ago. Liferay Legend Posts: 14933 Join Date: 9/2/06 Recent Posts

_sysAccessReqService = XxxLocalServiceUtil.getService();

The above line is not supposed to be used, but it's the only thing that works with how my code is currently setup. I'm converting code from Liferay 6.2 to 7.1, so there is alot of use of the xXXUtil classes. I'd prefer not to have to re-write the whole code base, hence the reason I'm calling getService()

Wait, I think you missed the part where I said that the XxxLocalServiceUtil classes were for legacy code?  If you are upgrading code, then keep the util classes and use them that way.

In new code, however, you want to stick with components and @Reference injections.
Kevin Neibarger, modified 6 Years ago. Regular Member Posts: 105 Join Date: 2/2/18 Recent Posts
So, I found a work-a-round that avoids having to code the following line

_xXXLocalService = XxxLocalServiceUtil.getService();

I will have to override the render method of the custom MVCPortlet class to save the service class instance in the Request attribute in order to use it in POJOs and JSP files. Since this is a @Component class the code will work and I'll have a non-null reference to the service class object. The only caveat is that now I have alot of extra work to do since all POJO methods need an extra parameter passed that holds the Request Attribute. 


@Reference

private ObservationLocalService _observationLocalService;


@Override

public void render(RenderRequest renderRequest, RenderResponse renderResponse)

throws IOException, PortletException {

// set service bean

renderRequest.setAttribute("observationLocalService", _observationLocalService);


super.render(renderRequest, renderResponse);

}
 
And my POJO class snippet

// Get the Object from the Request Attribute

ObservationLocalService _observationLocalService = (ObservationLocalService) request

.getAttribute("observationLocalService");

So, it's a compilcated trade off to avoid using the XxxLocalServiceUtil classes. If anyone can think of a less complicated way, let me know.. 
thumbnail
Olaf Kock, modified 6 Years ago. Liferay Legend Posts: 6441 Join Date: 9/23/08 Recent Posts
Kevin NeibargerSo, it's a compilcated trade off to avoid using the XxxLocalServiceUtil classes. If anyone can think of a less complicated way, let me know.. 
well, yes, it's always more complicated to use explicit local state (that needs to be injected) than relying on global state (as you do when calling the *Util classes). It might be complicated, but in the end cleaner.

That's not to say that it's pretty - as I understand the current way is your quickfix - but as soon as you realize the necessity of injections of those services, you can figure out more elegant solutions. If those are: Different styles of injections, turning your Pojos into Declarative Services, manually looking up the services, or something completely different: I'm not sure what's advisable in the current situation.
But the basic trajectory, away from using global constructs, is actually a good one.
thumbnail
David H Nebinger, modified 6 Years ago. Liferay Legend Posts: 14933 Join Date: 9/2/06 Recent Posts
I think I did a blog showing this actual use: https://community.liferay.com/blogs/-/blogs/accessing-services-in-jsps

I just didn't know that's what you were trying to do.