留言板

sharing service builder services through more portlets

Patrik Holecka,修改在9 年前。

sharing service builder services through more portlets

Junior Member 帖子: 46 加入日期: 13-10-21 最近的帖子
Hi all,
I have two portlets. Portlet1 is service builder portlets which holds all generated services and persistence layer. Then I have Portlet2 which wants to use these services. There are many threads with informations that this could be done by putting *.jar with services into application server's global lib. This solutions is not for me because with every change in these services you have to restart application server.
The other possibility to share services between portlets is to use "required-deployment-contexts" setting in liferay-plugin-package.properties. This one seems to be fine but I cannot get this working.
I added this setting into Porltet2 liferay-plugin-package.properties (required-deployment-contexts=Portlet1). Portlet is deployed without error but when I run method that uses Portlet1's services it throw exception NoClassDefFoundError on class that is needed by this services.

this is called in Portlet2
velId = CounterLocalServiceUtil.increment(Velvyslanectvo.class.toString());
velvyslanectvo = VelvyslanectvoLocalServiceUtil.createVelvyslanectvo(velId);

"Velvyslanectvo" class is generated in Portlet1 based on service.xml.
This exception is thrown:
java.lang.Exception: java.lang.NoClassDefFoundError: test/portal/model/Velvyslanectvo

When I add *.jar with services into Portlet2.war it works fine.

Thanks,
Patrik
Arun R S Chandran,修改在9 年前。

RE: sharing service builder services through more portlets

Regular Member 帖子: 149 加入日期: 13-3-12 最近的帖子
I was doing a copy of jar file in the second portlets lib folder. This will definitely resolve the issue. But I think for adding to plugin-package.properties we need to keep the file in a shared lib path where Liferay and other portlets have access(Note: I didn't tried this way.).
Patrik Holecka,修改在9 年前。

RE: sharing service builder services through more portlets

Junior Member 帖子: 46 加入日期: 13-10-21 最近的帖子
I was doing it this way too. But lets think about situation that you have to 10 portlets that are using these services. Now you have to make a change in service implementation. Now you can rebuild and redeploy all 10 portlets with new services jar. This is not very good solution...
So if I want to use "required-deployment-contexts" I have to have services.jar in global lib path in app server?

Arun R S Chandran:
I was doing a copy of jar file in the second portlets lib folder. This will definitely resolve the issue. But I think for adding to plugin-package.properties we need to keep the file in a shared lib path where Liferay and other portlets have access(Note: I didn't tried this way.).
Arun R S Chandran,修改在9 年前。

RE: sharing service builder services through more portlets

Regular Member 帖子: 149 加入日期: 13-3-12 最近的帖子
Patrik Holecka:
I was doing it this way too. But lets think about situation that you have to 10 portlets that are using these services. Now you have to make a change in service implementation. Now you can rebuild and redeploy all 10 portlets with new services jar. This is not very good solution...
So if I want to use "required-deployment-contexts" I have to have services.jar in global lib path in app server?

Arun R S Chandran:
I was doing a copy of jar file in the second portlets lib folder. This will definitely resolve the issue. But I think for adding to plugin-package.properties we need to keep the file in a shared lib path where Liferay and other portlets have access(Note: I didn't tried this way.).



I think that is the only way we can do. We have to keep the lib in tomcat/lib/ext folder and restart... emoticon
thumbnail
David H Nebinger,修改在9 年前。

RE: sharing service builder services through more portlets

Liferay Legend 帖子: 14919 加入日期: 06-9-2 最近的帖子
Arun R S Chandran:
I think that is the only way we can do. We have to keep the lib in tomcat/lib/ext folder and restart... emoticon


Definitely not. It is not the answer and should be avoided at all costs. Doing this is a lot more trouble than it is worth.
Patrik Holecka,修改在9 年前。

RE: sharing service builder services through more portlets

Junior Member 帖子: 46 加入日期: 13-10-21 最近的帖子
Hi David, thanks for your replies.
This is how my project setup is configured:
For building is used maven. Services project is added as dependency into consuming projects with normal scope, so services jar is added in every deployed artifact which is consuming these services.
My goal is to use this dependency with scope "provided" so the services jar is used only in maven build phase. So every consuming artifact will have not this jar in its war and it is expecting to have this services jar somewhere on classpath.

There is some discrepancy in you answers for me:
1. make the service jar global. Definitely requires app server restart, cleanup during deployment, and is generally a PITA but it will save on having to deploy the service provider and 10+ service consumers.

versus

I think that is the only way we can do. We have to keep the lib in tomcat/lib/ext folder and restart...
Definitely not. It is not the answer and should be avoided at all costs. Doing this is a lot more trouble than it is worth.

At one hand you are giving me advice to make services jar global (for 10+ consumers case) but on the other hand you are telling that it should be avoided at all costs.
thumbnail
David H Nebinger,修改在9 年前。

RE: sharing service builder services through more portlets (答复)

Liferay Legend 帖子: 14919 加入日期: 06-9-2 最近的帖子
Patrik Holecka:
My goal is to use this dependency with scope "provided" so the services jar is used only in maven build phase. So every consuming artifact will have not this jar in its war and it is expecting to have this services jar somewhere on classpath.


Your two options here are to make the service jar global (bad idea) or to put the service jar into ROOT/WEB-INF/lib and then add as a portal dependency jar (also bad). The reason they're both bad options is that it forces a server shutdown and manual jar moving (because it is always shipped with the service providing plugin).

There is some discrepancy in you answers for me:
1. make the service jar global. Definitely requires app server restart, cleanup during deployment, and is generally a PITA but it will save on having to deploy the service provider and 10+ service consumers.

versus

I think that is the only way we can do. We have to keep the lib in tomcat/lib/ext folder and restart...
Definitely not. It is not the answer and should be avoided at all costs. Doing this is a lot more trouble than it is worth.

At one hand you are giving me advice to make services jar global (for 10+ consumers case) but on the other hand you are telling that it should be avoided at all costs.


Note how I said that there is a very narrow set of use cases where the shared jar is appropriate. Because of the issues with deployment, it really should be avoided at all costs. A global jar should not be the first thought when it comes to handling the service jar, it should be used as a last resort because you've done all of the analysis and can stand behind the decision to pay the penalties in making the service jar global.

Having 10+ service consumers may actually be a scenario where it is worth paying that penalty, or it may not. But as I said, it's not the first answer you should jump to, it's a scale with weights on each side; as you add service consumers, that side gets heavier until eventually the scale tips in favor of a global jar. I don't know that 10 is the magic number, but clearly one or two service consumers certainly aren't there.

Remember that every server shut down represents an outage to your users. Every outage reflects negatively on your implementation. Bosses don't like to hear that the server went down so you could move jars around. In order to suffer this kind of deployment and not have an outage, then you're going to have to run a cluster and your deployment then involves taking nodes out of the cluster, waiting for users to drain off, do the deployment, add the node to the cluster, wash rinse and repeat until all nodes are updated. Definitely a painful deployment process to take on just for the sake of trying to use a global service jar when there really isn't any need.

To me, I think the question is really moot. When I create my service tier I spend extra time in the definition of the entities and the methods that will become part of the XxxLocalServiceImpl classes. Paying any penalty due to poor up front design is, to me, bad no matter how you're handling deployments. If you spend the time getting your API right, even building in stub methods that you might be needing later on, well then you finalize a service API that will work for the long term.

Liferay, for example, uses a similar approach to some degree. Many of their service methods include the ServiceContext object. Not because they love hiding information within an opaque object, but because it allows you to finalize an API that is capable of handling some future growth. I've used this same sort of thing to set attributes in the service object and extract them and use them in the service method implementation in lieu of creating a new method with additional parameters because, at the end of the day, it allows for a changing method implementation that will not require building services and deploying a service jar.
Patrik Holecka,修改在9 年前。

RE: sharing service builder services through more portlets

Junior Member 帖子: 46 加入日期: 13-10-21 最近的帖子
So from your point of view:
global libs = bad idea
services jar in each deployed artifact = good idea?

Can't there be problems with duplicate classes if one portlet has older version of services than other?

David H Nebinger:
Patrik Holecka:
My goal is to use this dependency with scope "provided" so the services jar is used only in maven build phase. So every consuming artifact will have not this jar in its war and it is expecting to have this services jar somewhere on classpath.


Your two options here are to make the service jar global (bad idea) or to put the service jar into ROOT/WEB-INF/lib and then add as a portal dependency jar (also bad). The reason they're both bad options is that it forces a server shutdown and manual jar moving (because it is always shipped with the service providing plugin).

There is some discrepancy in you answers for me:
1. make the service jar global. Definitely requires app server restart, cleanup during deployment, and is generally a PITA but it will save on having to deploy the service provider and 10+ service consumers.

versus

I think that is the only way we can do. We have to keep the lib in tomcat/lib/ext folder and restart...
Definitely not. It is not the answer and should be avoided at all costs. Doing this is a lot more trouble than it is worth.

At one hand you are giving me advice to make services jar global (for 10+ consumers case) but on the other hand you are telling that it should be avoided at all costs.


Note how I said that there is a very narrow set of use cases where the shared jar is appropriate. Because of the issues with deployment, it really should be avoided at all costs. A global jar should not be the first thought when it comes to handling the service jar, it should be used as a last resort because you've done all of the analysis and can stand behind the decision to pay the penalties in making the service jar global.

Having 10+ service consumers may actually be a scenario where it is worth paying that penalty, or it may not. But as I said, it's not the first answer you should jump to, it's a scale with weights on each side; as you add service consumers, that side gets heavier until eventually the scale tips in favor of a global jar. I don't know that 10 is the magic number, but clearly one or two service consumers certainly aren't there.

Remember that every server shut down represents an outage to your users. Every outage reflects negatively on your implementation. Bosses don't like to hear that the server went down so you could move jars around. In order to suffer this kind of deployment and not have an outage, then you're going to have to run a cluster and your deployment then involves taking nodes out of the cluster, waiting for users to drain off, do the deployment, add the node to the cluster, wash rinse and repeat until all nodes are updated. Definitely a painful deployment process to take on just for the sake of trying to use a global service jar when there really isn't any need.

To me, I think the question is really moot. When I create my service tier I spend extra time in the definition of the entities and the methods that will become part of the XxxLocalServiceImpl classes. Paying any penalty due to poor up front design is, to me, bad no matter how you're handling deployments. If you spend the time getting your API right, even building in stub methods that you might be needing later on, well then you finalize a service API that will work for the long term.

Liferay, for example, uses a similar approach to some degree. Many of their service methods include the ServiceContext object. Not because they love hiding information within an opaque object, but because it allows you to finalize an API that is capable of handling some future growth. I've used this same sort of thing to set attributes in the service object and extract them and use them in the service method implementation in lieu of creating a new method with additional parameters because, at the end of the day, it allows for a changing method implementation that will not require building services and deploying a service jar.
thumbnail
Jan Geißler,修改在9 年前。

RE: sharing service builder services through more portlets

Liferay Master 帖子: 735 加入日期: 11-7-5 最近的帖子
Can't there be problems with duplicate classes if one portlet has older version of services than other?

This Problem can arise. That is true. But you also can have Problems if you put them in a global Path. What happens, if you change some Method signatures, push you lib to the server lib folder, deploy your portlet, and all other dependent portlets go haywire because the are using a method signature, that no longer exists? You would have no safety there.

So if you ship the service jar within very portlet, you first have to put the Service jar to that portlet, and your IDE will tell you if you have to change anything in that portlet because of the changes you did to the service layer. In my mind, this approach will give you better control about bugs introduced by changing the service layer.
thumbnail
David H Nebinger,修改在9 年前。

RE: sharing service builder services through more portlets

Liferay Legend 帖子: 14919 加入日期: 06-9-2 最近的帖子
Jan nailed it.

It's not duplicate classes you'll have to deal with, though, it's actually a little more esoteric than that. The service jar actually uses reflection to invoke methods on the remote service. So if the method that you're trying to invoke has a changed method signature (new params or whatever), then you'll get the NoSuchMethod exception.
Patrik Holecka,修改在9 年前。

RE: sharing service builder services through more portlets

Junior Member 帖子: 46 加入日期: 13-10-21 最近的帖子
Thank you guys. You helped me out from this misery. Every approach have its prons and cons but I decided to pack service jar with every portlet that consumes these services .
thumbnail
Jack Bakker,修改在8 年前。

RE: sharing service builder services through more portlets

Liferay Master 帖子: 978 加入日期: 10-1-3 最近的帖子
I personally still see challenges with the duplication of service jar across many portlets. But yeah, moving to global classpath also has its own challenges.

when the number of portlets needing access to common services gets large and there are different developers dealing with each consumer portlet : the question of how best to distribute/share services becomes its own challenge = this is involved in the monolithic vs microservices question...
Arun R S Chandran,修改在8 年前。

RE: sharing service builder services through more portlets

Regular Member 帖子: 149 加入日期: 13-3-12 最近的帖子
Jack Bakker:
I personally still see challenges with the duplication of service jar across many portlets. But yeah, moving to global classpath also has its own challenges.

when the number of portlets needing access to common services gets large and there are different developers dealing with each consumer portlet : the question of how best to distribute/share services becomes its own challenge = this is involved in the monolithic vs microservices question...



I think we may use the service jar files as maven dependencies and use the portlets to get that added in their POM files. May be this way we are forcing the developer to choose maven for their project, but seems it could help the projects for a smooth running.
thumbnail
Jack Bakker,修改在8 年前。

RE: sharing service builder services through more portlets

Liferay Master 帖子: 978 加入日期: 10-1-3 最近的帖子
Arun R S Chandran:
Jack Bakker:
I personally still see challenges with the duplication of service jar across many portlets. But yeah, moving to global classpath also has its own challenges.

when the number of portlets needing access to common services gets large and there are different developers dealing with each consumer portlet : the question of how best to distribute/share services becomes its own challenge = this is involved in the monolithic vs microservices question...


I think we may use the service jar files as maven dependencies and use the portlets to get that added in their POM files. May be this way we are forcing the developer to choose maven for their project, but seems it could help the projects for a smooth running.


I recommend using maven moving forward, but that is a somewhat separate topic from what you quoted me on here and replied to.

wrt to initial topic at hand (allowing multiple portlets to consume common service builder services) : I suggest most should follow the guidance of David and others on not putting service jars in global classpath and instead distribute service jar(s) into all consumer/dependent portlets. Such is not a maven vs sdk discussion but an architectural discussion relating to service provision and service consumption.
thumbnail
David H Nebinger,修改在9 年前。

RE: sharing service builder services through more portlets

Liferay Legend 帖子: 14919 加入日期: 06-9-2 最近的帖子
Patrik Holecka:
I was doing it this way too. But lets think about situation that you have to 10 portlets that are using these services. Now you have to make a change in service implementation. Now you can rebuild and redeploy all 10 portlets with new services jar. This is not very good solution...


First you have to determine if this is a valid use case. Most organizations where a service is implemented, they actually have only a handful of service consumers to worry about.

If you do have 10 or more consumers, you basically have two choices:

1. make the service jar global. Definitely requires app server restart, cleanup during deployment, and is generally a PITA but it will save on having to deploy the service provider and 10+ service consumers.
2. spend the time to architect your services in your beginning iterations of development. If you think that some time in the future you'll be needing a method, add it initially and just throw an exception saying it's not implemented yet. As long as the contract doesn't change, you can come back later, replace the exception with the implementation and you don't have to build services and you don't have to push out all the deployments. And hey, if you do get into a situation where you have to do all the deployments, there are shortcuts there (instead of deploying all consumers, just have the admins replace the service jars manually - they'd have to restart there, too, but it still allows you to stay on the normal path).

So if I want to use "required-deployment-contexts" I have to have services.jar in global lib path in app server?


No, that's not true. Check my other post. You always want to use required-deployment-context to ensure the portal doesn't start your plugin unless the dependent one is already started. Has nothing to do with whether service jar is global or not.

Arun R S Chandran:
I was doing a copy of jar file in the second portlets lib folder. This will definitely resolve the issue. But I think for adding to plugin-package.properties we need to keep the file in a shared lib path where Liferay and other portlets have access(Note: I didn't tried this way.).


Untrue and generally should be avoided.
thumbnail
David H Nebinger,修改在9 年前。

RE: sharing service builder services through more portlets

Liferay Legend 帖子: 14919 加入日期: 06-9-2 最近的帖子
Patrik Holecka:
I have two portlets. Portlet1 is service builder portlets which holds all generated services and persistence layer. Then I have Portlet2 which wants to use these services. There are many threads with informations that this could be done by putting *.jar with services into application server's global lib. This solutions is not for me because with every change in these services you have to restart application server.


This is why everyone who recommends this path is simply wrong. There are use cases where this is appropriate, but they are few and far between and not for the average site. Everyone who suggests this as the first option is just looking at a quick hack, and they definitely don't consider the long run consequences or deployment issues (developers rarely care about the impact to systems folks).

The other possibility to share services between portlets is to use "required-deployment-contexts" setting in liferay-plugin-package.properties. This one seems to be fine but I cannot get this working.


This does work, but depending upon how you are creating your portlets it may be automatic or manual.

At it's core, required deployment contexts tells Liferay that this plugin should not run unless the required one has started. This is a runtime/deployment thing that the portal will enforce.

Now if you are using the SDK and the Liferay IDE or Liferay Development Studio, well they take the hint from the required deployment context and copy the service jar into WEB-INF/lib automatically and include it in the project libs (well, as long as you also have the project loaded into your IDE session).

Using any other mechanism, well then you're responsible for the jar install. In Maven, for example, this can be a drag. In your plugin that provides the service, well there in the service jar project you're going to have to do mvn install to get it into your local repo. Note that this also means you are really watching, monitoring and maintaining your version numbers in you pom files because you want to ensure you're doing a new version for every mvn install.

On the consuming maven project, well there you have to add a dependency jar to your pom so the lib is pulled in and included for builds and deployment in addition to the required deployment context.

velId = CounterLocalServiceUtil.increment(Velvyslanectvo.class.toString());
velvyslanectvo = VelvyslanectvoLocalServiceUtil.createVelvyslanectvo(velId);


Note CounterLocalServiceUtil.increment() should be called using Xxx.class.getName(), not toString().

java.lang.Exception: java.lang.NoClassDefFoundError: test/portal/model/Velvyslanectvo

When I add *.jar with services into Portlet2.war it works fine.


This tells me that you fall under the manual method I mentioned above. Make sure your service jar is added as a project dependency and included in the deployment artifact and you'll be fine.