Forums

Home » Liferay Portal » English » 3. Development

Combination View Flat View Tree View
Threads [ Previous | Next ]
toggle
Gabriel Palacios
JPA Again!! *sigh*
November 6, 2012 9:59 AM
Answer

Gabriel Palacios

Rank: Junior Member

Posts: 31

Join Date: November 18, 2011

Recent Posts

Soooo,

I'm going home after struggling all day long with this problem.
Liferay 6.1.1 EE

I needed to:
- connect with another database apart from liferay's. I made it with jpa 2.
- call the related services from a hook.

I did:
- business facade (service.xml, no entities)
- included my external database model as interfaces
- implemented this model with jpa Entities
- injected my jpa controllers into my *LocalServiceImpl classes

in order to:
- call via web service from the hook
- keep coherence with the rest of the serviceBuilder entities.

So far, so good.

Nooooooooow,
my jpa namedQueries' (<mapping-file /> xmls) can't be reached when loading my custom jpa transaction manager. Whatever I try, it will just keep saying:
There is no mapping file called [/whatever-path/whatever-file.xml] in classpath for persistence unit named [whatever-pu]

Clearly:
- my <mapping-file /> xmls aren't in the ROOT's classpath (the place where they are supposed to be for ROOT's spring to get my persistence unit running)

Then why?
- can I i.e. read *.properties?

Do someone know how to deal with this?
Solutions that won't be accepted:
- use only ServiceBuilder
- take a bullet in the head
- retire as a programmer

Regards.
David H Nebinger
RE: JPA Again!! *sigh*
November 6, 2012 10:58 AM
Answer

David H Nebinger

Community Moderator

Rank: Liferay Legend

Posts: 11511

Join Date: September 1, 2006

Recent Posts

Okay, so basically I think you're going to be stuck hammering your head into the wall...

SB creates two separate jars, a service jar that contains interfaces and methods to cross the web application class loader boundary which is then shared w/ other plugins, and an implementation jar where the XxxLocalServiceImpl classes are.

The interfaces all implement serializable to help crossing the web app boundary.

When you invoke SB from another plugin, you're crossing the web app class loader boundary to get into the implementation classes. The implementation classes use the concrete implementations of the entity classes and return them to the caller. There's class loader proxying logic in the service jar which handle the marshalling to get interface objects that can be manipulated within the other class loader.

Your hacked up implementation is going to end up failing because you're not going to be handling the crossing of the class loader boundary correctly.

Since you're unwilling to consider a SB-only solution, you should really drop the SB thing altogether and just do a native JPA implementation.

The downside is that you're going to end up replicating code to all plugins that will be doing the JPA access.

You're going to want to stick with an application container managed DataSource so you can use a shared connection pool in all of the plugins.

Note that hooks (especially JSP hooks) copy some files to the ROOT web application which is the context the hook runs in, but does not copy all files to ROOT. If you need to inject XML files into ROOT, you'll need to use an EXT plugin instead of a hook.
Gabriel Palacios
RE: JPA Again!! *sigh*
November 6, 2012 12:32 PM
Answer

Gabriel Palacios

Rank: Junior Member

Posts: 31

Join Date: November 18, 2011

Recent Posts

David H Nebinger:
SB creates two separate jars, a service jar that contains interfaces and methods to cross the web application class loader boundary which is then shared w/ other plugins, and an implementation jar where the XxxLocalServiceImpl classes are.

The interfaces all implement serializable to help crossing the web app boundary.


Ok.

David H Nebinger:
When you invoke SB from another plugin, you're crossing the web app class loader boundary to get into the implementation classes. The implementation classes use the concrete implementations of the entity classes and return them to the caller. There's class loader proxying logic in the service jar which handle the marshalling to get interface objects that can be manipulated within the other class loader.


Ok.

David H Nebinger:
Your hacked up implementation is going to end up failing because you're not going to be handling the crossing of the class loader boundary correctly.


Please explain why my implementation is hacked up.

David H Nebinger:
Since you're unwilling to consider a SB-only solution, you should really drop the SB thing altogether and just do a native JPA implementation.


It already is a native JPA implementation.

David H Nebinger:
Note that hooks (especially JSP hooks) copy some files to the ROOT web application which is the context the hook runs in, but does not copy all files to ROOT. If you need to inject XML files into ROOT, you'll need to use an EXT plugin instead of a hook.


Note that I explained the necessity of a hook, but never involved it in the actual problem.

I'm gonna try and explain it better tomorrow, I was kind of tired before and may have not detailed it enough.
Gabriel Palacios
RE: JPA Again!! *sigh*
November 7, 2012 2:02 AM
Answer

Gabriel Palacios

Rank: Junior Member

Posts: 31

Join Date: November 18, 2011

Recent Posts

I'll try again.

Most of the business model is already defined in my client's database (which is not liferay's one, obviously).
I implemented a DAO layer with JPA 2. On top of it a business/service layer. I also defined the datasource, entity manager factory, transaction manager, transaction advices and bean injections (dao -> service) in a spring xml.

I somehow succeeded in "integrating" it with Liferay, exporting the whole pack into a jar, and locating that jar into the global server classpath (/tomcat/lib/ext). This way, any portlet or hook could just generate an application context by referencing the spring xml, and get the required beans.
This actually works quite well.

But, I didn't like it, since every change to the services (most of them are minor jpql change) implies a server restart.

Meanwhile, I was in another project in which a hook would invoke a plugins services via web-service.
So I said to myself "why don't give it a try?. That way, the application won't be tied to a server restart".

So, since in my project there are several SB entities (apart from the JPA implementation ones), and I wanted to keep things morphologically similar, I choosed to "use" the Service Builder. I investigated and found that you can define entities without columns, which gets you a business facade, just what I needed.

I went through:
  • defining my entity interfaces in the already generated whatever-service.jar.
    just as SB does, althought omitting all the CLP thing. I wouldn't need to reference them from other plugins, just to generate the web service.
  • implement this entity interfaces in the plugin, with the JPA entities
  • implementing my methods in the WhateverLocalServiceImpl, with calls to the JPA layer
  • defining the datasource, entity manager, etc.. in a custom META-INF/*.spring xml, alongside the ones SB generates
    (and creating a service-ext.properties to include my custom spring xml in the spring.configs property) (JPA beans are injected into WhateverLocalServiceImpl through Liferay's @BeanReference annotation, it wouldn't work with spring annotations)


Everything works fine (even if I stil have to test the web service invocation and I'm not really sure it would work) except for one thing: my JPA named queries.
These are stored in several xmls, and referenced in persistence.xml with the <mapping-file /> tag.

When spring builds the jpa transaction manager, it complains it can't find this xmls, whatever path I put them in. This is the message:
There is no mapping file called [/whatever-path/whatever-file.xml] in classpath for persistence unit named [whatever-pu]

I tried setting the jpa xmls in ROOT's classpath, and spring stopped complaining. But when executing a named query, no entities can be resolved. This is the message:
The state field path 'whatever.ever' cannot be resolved to a valid type
Translated: I don't find your Entity classes in my ClassLoader.

Now. I did't want a solution for my integration as I will stick with the global lib/ext jar, even if I have to restart the server each time.
But I wanted to understand why this was happening and finally came to look at Liferay's source code. And I came to some conclusions.

Could someone ratify the nex assumptions? For knowledge sake.

SB service implementations are instantiated and loaded into a BeanLocator through PortletBeanLocatorUtil (/lib/ext/portal-service.jar) as an Object to make it available to every WAR deployed. When a SB portlet invokes a service method, the service is loaded from the PortletBeanLocatorUtil and the method is invoked through reflection. The response is casted to its correspondant ClassLoader model interface (if applicable).

Regards.
David H Nebinger
RE: JPA Again!! *sigh*
November 7, 2012 6:29 AM
Answer

David H Nebinger

Community Moderator

Rank: Liferay Legend

Posts: 11511

Join Date: September 1, 2006

Recent Posts

Gabriel Palacios:
SB service implementations are instantiated and loaded into a BeanLocator through PortletBeanLocatorUtil (/lib/ext/portal-service.jar) as an Object to make it available to every WAR deployed. When a SB portlet invokes a service method, the service is loaded from the PortletBeanLocatorUtil and the method is invoked through reflection. The response is casted to its correspondant ClassLoader model interface (if applicable).


This is true, but the key to the casting is that it is against entities defined in the service.xml. SB XxxLocalServiceImpl methods can only return entities or primitives (or their object counterparts).

Any other type of object will not have the appropriate stuff constructed to a) return it across the class loader proxy or b) have the web service/soap wrappers to return via web service call.

Basically SB owns all of the code, and anything that SB doesn't generate for you will not be usable in any real sense.

That's why I said your implementation is 'hacked up'. I wasn't trying to offend, but was trying to point out that your square peg is not going to fit into the SB's round hole...

To me, this is one of the biggest shortcomings with SB - not being able to define entities that are non-DB related. If we could, an entity definition matching your JPA object would have SB build the underlying framework for sharing these objects in a portal environment, and you would just be left to binding into JPA in the XxxLocalServiceImpl classes...

Ah, but it is still a dream....
Paco Alías
RE: JPA Again!! *sigh*
December 3, 2012 12:25 PM
Answer

Paco Alías

Rank: Junior Member

Posts: 25

Join Date: December 18, 2011

Recent Posts

Hi,

I'm trying to use some non-liferay & non-service entities inside a service built with the SB... in other words, MyServiceLocalImp needs some non-liferay entities to work which are not (detaily) defined in the service.xml. Can I define them without detailing all fields?

Searching the forums, I found this post related to this. So...

@David
Is there a way to do it without "hacking" ? :-o

@Gabriel
Please, can you explain in a more detailed way, how did you achieve your target?

Thanks in advance.
Gabriel Palacios
RE: JPA Again!! *sigh*
December 3, 2012 12:41 PM
Answer

Gabriel Palacios

Rank: Junior Member

Posts: 31

Join Date: November 18, 2011

Recent Posts

All the work and juggling ive done had any other purpose but to access a differenet DB schema than Liferay's.
Maybe if you explain what are your needs I can help you a little more.
Paco Alías
RE: JPA Again!! *sigh*
December 3, 2012 11:19 PM
Answer

Paco Alías

Rank: Junior Member

Posts: 25

Join Date: December 18, 2011

Recent Posts

Hi Gabriel,

This is my case:
- non-liferay database with lots of entities. This database is managed with its own web application.
- liferay service to read some information about this entities, process it and create new information as a result.

I'm looking for the correct way to make this entities accesible from my service without defining all attributes in each entity in service.xml.
I've tried to define empty entities (as you said in this post) but no persistence classes are generated with the SB, so maybe I need a Custom Query or something like that.
I've defined a datasource in ext-spring.xml pointing to an external database (that with lots of entities) and I can create the new information in the new tables, but no way to read that from my original database.

Apologies for my explanation but I haven't get enogh coffee yet ;-)

Thanks for yout quick answer ;-)
David H Nebinger
RE: JPA Again!! *sigh*
December 4, 2012 5:23 AM
Answer

David H Nebinger

Community Moderator

Rank: Liferay Legend

Posts: 11511

Join Date: September 1, 2006

Recent Posts

The only attributes you need to define in SB are the PK fields and the entities that you want to go after. You do not need to add every single entity into service.xml, just the ones that you need.

SB in itself is nothing more than a shim layer over Hibernate. The shim layer allows you to use the service across the web app class loader boundary. But anything that you can do in Hibernate (i.e. some tables of a database instead of all, partial entity definition, etc.) you can do in service builder.
Gabriel Palacios
RE: JPA Again!! *sigh*
December 4, 2012 6:59 AM
Answer

Gabriel Palacios

Rank: Junior Member

Posts: 31

Join Date: November 18, 2011

Recent Posts

Hey,sorry for not answering, I just woke up ;)

I offer you 2 options, they're the cleaner thing I could come to:

implement you DAO and serv ice layers,transaction manager, security, etc ... with the framework you like the most and:
- generate a web service that exposes your service methods (and deploy it in a server, and generate the client, etc), or
- pack it in a jar and,
- if you are going to use this services in both portlets and hooks, put it in your server libraries and deal with dependencies, or
- if you are going to use it only in portlets, pack all the portlets in the same project/war, and put it in WEB-INF/lib

If you're planning to use this services in both portlets and hooks, I recommend to use the web service alternative. This way you wont be needing a server restart each time you change you services.

There must be a "Liferay way" to do this, but I don't know it. I tried several things, asked in the forum, but got only to the solutions above.

Good luck ;)
David H Nebinger
RE: JPA Again!! *sigh*
December 4, 2012 7:52 AM
Answer

David H Nebinger

Community Moderator

Rank: Liferay Legend

Posts: 11511

Join Date: September 1, 2006

Recent Posts

Web services adds quite a bit of overhead just to facilitate database access... I would never recommend using a web service client for an internal service simply because of the overhead that is involved.

ServiceBuilder is designed to support the database access and provide this access across the class loader boundary put in place by the application container.

As I've said before, using some other framework might be the easy approach, but if you have 10 separate portlet plugin projects all using the same framework code to access the same data, your server resource usage is actually increased 10 times, whereas the SB implementation is still a singleton.

Merging the 10 separate portlets into a single plugin resolves the resource issue, but it may end up coupling your portlets in ways that you don't want (i.e. it can make team development hard because the team will spend more time merging code changes).

I also don't understand your comment about server restarts. Server restarts are only necessary when you deploy an EXT plugin (not involved w/ SB code) or to deploy a jar to the application container's global library directory (again, not involved w/ normal SB usage unless you are hooking the Liferay ROOT application to provide access to the service code, and that is an artifact of the hook and not the SB code).

Besides, if you're going to use another framework as part of a hook you face these issues anyway since you'd have to bundle your code in an EXT plugin to make it available when the hook is deployed (since not all code deployed with a hook is accessible in the ROOT app anyway).
Gabriel Palacios
RE: JPA Again!! *sigh*
December 4, 2012 8:09 AM
Answer

Gabriel Palacios

Rank: Junior Member

Posts: 31

Join Date: November 18, 2011

Recent Posts

Then how would you do it?

Be as concise as possible, please.
Paco Alías
RE: JPA Again!! *sigh*
December 4, 2012 9:55 AM
Answer

Paco Alías

Rank: Junior Member

Posts: 25

Join Date: December 18, 2011

Recent Posts

Thanks Gabriel for the explanation
Paco Alías
RE: JPA Again!! *sigh*
December 4, 2012 9:57 AM
Answer

Paco Alías

Rank: Junior Member

Posts: 25

Join Date: December 18, 2011

Recent Posts

Thanks also David for your explanation.
Please, keep the debate alive!

It'll be wonderful to have a post or a wiki page with all this knowledge

Thank you!
David H Nebinger
RE: JPA Again!! *sigh*
December 4, 2012 11:27 AM
Answer

David H Nebinger

Community Moderator

Rank: Liferay Legend

Posts: 11511

Join Date: September 1, 2006

Recent Posts

Gabriel Palacios:
Then how would you do it?


We use Service Builder. Just like learning Hibernate or JPA, there's a learning curve. A lot of hibernate/jpa folks look at SB and think it's a kludge or that it's difficult to work with compared to what they know. From my own experience, I too started down that road.

It's only after encountering and dealing with a bunch of these kinds of issues (code copying, multiple framework loads, etc.) that I realized the value that SB provides:

1. no multiple frameworks - all SB code is in one plugin, only the service jar (which has the shim interfaces to cross the web app class loader boundaries).
2. no extra resource usage - you don't have multiple plugins each loading/using their own instances of framework objects to manage database connections, ORM, transaction management, etc.
3. integration w/ Liferay features - model listeners, transaction management, expando support, counter facilities, etc.
4. shared services across all plugins automagically (except for hooks).

Yes it is different that Hibernate and JPA. Yes, if you already have entities defined in those other frameworks you'll have to redo them in Service Builder. Yes there's a learning curve required to figure out how to get the most out of Service Builder. No, some things you did in Hibernate or JPA will not translate well (if at all) to Service Builder.

But it does have a lot of benefits, especially in a portal environment, that these other frameworks are just not going to give you...

P.S. the last time I told a hibernate/JPA guy to use SB he got all mad at me saying it was not his question. Before I get you or anyone else mad at me for this suggestion, I'm not trying to trash the frameworks or anything like that. I am trying to encourage you to use SB because it does have benefits in the portal environment, and these benefits are not always obvious, especially to developers familiar with Hibernate and/or JPA...
Gabriel Palacios
RE: JPA Again!! *sigh*
December 4, 2012 11:47 AM
Answer

Gabriel Palacios

Rank: Junior Member

Posts: 31

Join Date: November 18, 2011

Recent Posts

I still dont know how to manage entities nor how to connect with another database following SB's flavour.
Could you be more concise? Provide some examples? Some guidelines on how to do it?

However,
4. shared services across all plugins automagically (except for hooks).

That is enough for me.
Paco Alías
RE: JPA Again!! *sigh*
December 4, 2012 11:48 AM
Answer

Paco Alías

Rank: Junior Member

Posts: 25

Join Date: December 18, 2011

Recent Posts

Hi David,

I agree with the utility of SB
David H Nebinger:
Yes, if you already have entities defined in those other frameworks you'll have to redo them in Service Builder.


I've missed a script which can analyze entities packed in a JAR and then write some XML data describing them for SB. Maybe not directly in service.xml but in another file includen in it. Do you think it's a good idea?

Thank you!
David H Nebinger
RE: JPA Again!! *sigh*
December 4, 2012 1:14 PM
Answer

David H Nebinger

Community Moderator

Rank: Liferay Legend

Posts: 11511

Join Date: September 1, 2006

Recent Posts

Gabriel Palacios:
I still dont know how to manage entities nor how to connect with another database following SB's flavour.
Could you be more concise? Provide some examples? Some guidelines on how to do it?


There are many examples of how to do this on liferay.com and on external blogs, such as http://liferayazam.wordpress.com/2011/05/30/connect-to-different-database-through-service-xml-liferay6/. All of the examples boil down to two steps:

1. Declare the datasource to use in the service.xml the entity will use.
2. Define the datasource in ext-spring.xml.

I've used this method over and over again, and it just works. I've got one SB-based plugin that uses 6 different databases spread over 70 different entities. It definitely works just as everyone has outlined. If you're having specific problems, please post them because I'd be happy to help resolve them...

However,
4. shared services across all plugins automagically (except for hooks).

That is enough for me.


This is the same problem that you'd have using hibernate or JPA in hooks. It is not a problem with SB itself, it's not understanding that hooks actually apply directly to the ROOT web application. To make external code available to ROOT via hooks, you can either put the code in the global lib directory (requiring a restart, regardless of what framework you're using) or deployment via EXT.
David H Nebinger
RE: JPA Again!! *sigh*
December 4, 2012 1:18 PM
Answer

David H Nebinger

Community Moderator

Rank: Liferay Legend

Posts: 11511

Join Date: September 1, 2006

Recent Posts

Paco Alías:
I've missed a script which can analyze entities packed in a JAR and then write some XML data describing them for SB. Maybe not directly in service.xml but in another file includen in it. Do you think it's a good idea?


I'm not sure this would work... Entities created via service builder are actually interfaces. The interfaces are exposed in the service jar. The implementation classes for the interfaces adhere to a strict class heirarchy to make them compatible with the Liferay SB boiler plate code.

So generating a service.xml file (or included file) from an existing POJO would end up forcing you to dump your POJO and use the interface instead.

A script to generate service.xml from your database tables, that would prove to be valuable...
Gabriel Palacios
RE: JPA Again!! *sigh*
December 4, 2012 1:39 PM
Answer

Gabriel Palacios

Rank: Junior Member

Posts: 31

Join Date: November 18, 2011

Recent Posts

Finally something. Thank you.

But please stop implying things like I do not understand "hooks actually apply directly to the ROOT web application", right?
Besides, the fact that SB can't be hooked is ONE of SEVERAL reasons I didn't want to do it with SB.
David H Nebinger
RE: JPA Again!! *sigh*
December 4, 2012 2:20 PM
Answer

David H Nebinger

Community Moderator

Rank: Liferay Legend

Posts: 11511

Join Date: September 1, 2006

Recent Posts

Gabriel Palacios:
Finally something. Thank you.

But please stop implying things like I do not understand "hooks actually apply directly to the ROOT web application", right?
Besides, the fact that SB can't be hooked is ONE of SEVERAL reasons I didn't want to do it with SB.


Sorry, Gabriel, I don't know what you're expertise is and wasn't trying to imply what you don't know. Often times when replying I try to state all of the facts in case someone new happens upon a thread and may not have the same background as either of us. By stating all of the facts I kinda hope that they won't end up having to pull information from other sources...

I know there are reasons not to want to use SB (i.e. you already have an existing Hibernate or JPA app you're trying to port to Liferay). There are times when SB is a square peg and you're dealing with a round hole and SB is just not the right fit.

At the same time, there's always a flow of new users coming to Liferay who are starting a project from scratch and have some data access requirements. If they have some JPA experience and google for "liferay JPA" they can stumble into threads like this one and believe that JPA could be used without any problems or worries. Even though it can be used, it does come with hidden costs that they really should be aware of, so I just try to highlight them...

But certainly I do not wish to offend anyone...