Fórumok

Service jar sharing between service and client

Przemysław Sporysz, módosítva 8 év-val korábban

Service jar sharing between service and client

New Member Bejegyzések: 2 Csatlakozás dátuma: 2013.11.02. Legújabb bejegyzések
Hi, it's my first post here ;)

I use Liferay 6.2.1 CE and noticed one day that client *Util classes generated from Service Builder are caching the instances of service implementation (to be more precise bunch of proxied classes). Just like:

    public static SubscriptionLocalService getService() {
        if (_service == null) {
            InvokableLocalService invokableLocalService = (InvokableLocalService) PortletBeanLocatorUtil.locate(ClpSerializer.getServletContextName(),
                    SubscriptionLocalService.class.getName());

            if (invokableLocalService instanceof SubscriptionLocalService) {
                _service = (SubscriptionLocalService) invokableLocalService;
            } else {
                _service = new SubscriptionLocalServiceClp(invokableLocalService);
            }

            ReferenceRegistry.registerReference(SubscriptionLocalServiceUtil.class,
                "_service");
        }

        return _service;
    }


I found that cleanup of the reference done via
ReferenceRegistry.releaseReferences();
is called only inside com.liferay.portal.spring.context.PortalContextLoaderListener class which is unfortunately part of the portal-impl.jar.

I wonder what would happen if the project were divided into multiple wars deployed in Tomcat and we decided to redeploy the war with implementation? Shall i redeploy all client portlets as well? What if one decide to put the service client jar only in ext/lib? Using ext/lib in Tomcat is necessary to be able to pass objects other than model or primitive types in the service method signature (otherwise we would end up with conversion exception due to different class-loaders of client/service wars, see ClpSerializer class).

I also think that above issues are the cause of the errors mentioned in topic:
http://www.liferay.com/community/forums/-/message_boards/message/16577981
thumbnail
Alexey Kakunin, módosítva 8 év-val korábban

RE: Service jar sharing between service and client

Liferay Master Bejegyzések: 621 Csatlakozás dátuma: 2008.07.07. Legújabb bejegyzések
Hi!

We widelly using splitting of project to smaller parts (plugins) and sharing services between them - almost no issues.
I mean - in case we redeploy some implementation plugin it is not required to redeploy all client plugins.
OK - only in case if interfaces was not changed - if interface of implementation was changed you will need to rebuild all client plugins and redeploy them as well.

In our case we are using maven and dependencies between plugins during building time managed by maven.

====
Alexey Kakunin
Liferay Experts in Russia
thumbnail
David H Nebinger, módosítva 8 év-val korábban

RE: Service jar sharing between service and client

Liferay Legend Bejegyzések: 14914 Csatlakozás dátuma: 2006.09.02. Legújabb bejegyzések
The client jar and the service provider portlet are inherently tied together.

When you make a change to service.xml and rebuild services or when you add a public method to one of the XxxImpl classes or modify a method signature of an existing public method or remove a public method, you have to rebuild services.

When you rebuild services, a build number value is incremented. This number is used by the service consumers to verify they have the right version of the service jar to access the service provider correctly.

Often times developers "over-rebuild" services. If you change the implementation of a method or something, they build services again unnecessarily. It's mostly because it hasn't been made clear that building services only updates the APIs in the service jar. The internals of the service method implementation or changes to protected/private objects have no bearing on the API itself, so those kinds of changes do not require building services.

But when you build services, the build number increments and all of the service consumers need an updated service jar, thus making a simple deployment of one plugin more complex to update other service consumers.

About the references, well these can be problems. If you have a portlet actively consuming services and you deploy an update to the service provider, this will definitely throw the consumer off. It is not a hard and fast rule, however, sometimes the updated service has caused me problems but sometimes it hasn't. In general I typically recommend performing an app container restart after any deployment as it tends to ensure the app container is clean and ready to service requests without lingering artifacts from previous instances.

Everyone likes to think about using the global class loader for handling the service jar. Sure it saves on the other deployments, but it has issues of it's own. First, the service providing plugin has the service jar in it. So you have to deploy the service provider, shut down the container and move the service jar to the global directory. This is a required restart and manual file moves, something that may be "process stink" to an ops team with a formal deployment process.

The post that you referred to, well that covers the build number issue. It's not related to the invalid references, it is a version check meant to ensure the service consumer jar is compatible with the service provider. I typically use a service-ext.properties file to fix the build number to a specific version. That way, if the services get rebuilt unnecessarily, I don't run into build number errors between consumer and provider. However, it also means that I assume responsibility for managing the build number and must update manually. For some API changes, I can still leave the version number alone. For example, I know that reflection is used to invoke a method on an instance of a class from the service provider - because of that I know I can add a new method and, as long as the service consumer doesn't care about the new method, it can continue using the service jar it has (I would only have problems if I remove a method (which I never do) or modify a method signature (which I also don't do, I might overload method names but I won't change outright), otherwise reflection using the proxy will continue to work just fine).

Using the global class loader for non-service objects, well I'm not that keen on that. All to often it goes from sharing a simple model jar to soon containing business logic, dependencies, etc. It gets unwieldy and still comes with a required container restart just to do the deployment (because a global jar is open all the time and filesystem will not allow you to overwrite while the container is running).

Instead I favor the "fake entity" approach outlined here: https://www.liferay.com/community/forums/-/message_boards/message/12095602. This is an elegant solution to define a shared class type that you can give to service consumers yet still keep the business logic wrapped up in a single service provider.
Przemysław Sporysz, módosítva 8 év-val korábban

RE: Service jar sharing between service and client

New Member Bejegyzések: 2 Csatlakozás dátuma: 2013.11.02. Legújabb bejegyzések
Thank you for answers. I wasn't aware of the Fake Object pattern in Liferay. Although it is more work to create a "simple DTO" that way, i think i like it anyway -- it foster the Restful-like API without much of RPC. I mean a single entity in Service Builder is similar to resource in RESTful API. About the referenced post: looks like i messed up links, i was thinking about that post.
thumbnail
David H Nebinger, módosítva 8 év-val korábban

RE: Service jar sharing between service and client

Liferay Legend Bejegyzések: 14914 Csatlakozás dátuma: 2006.09.02. Legújabb bejegyzések
I actually just posted a blog about the fake entities: https://www.liferay.com/web/26526/blog/-/blogs/fake-servicebuilder-entities.