Martin Straus:
Well, I think I hurt your feelings somehow. My appologies if that's the case.
Nope, I've got a pretty thick skin. I've been having this argument a lot, and sometimes it's just tough selling people on the benefits of SB...
Now, what you say about the classloader boundary makes a little more sense than your previous high-level explanation about "sharing services". But bear with me for a second...
What I critize is the "builder" part in "service builder". And I don't see how it solves the issue you mention. It just generates classes, which still can't be shared between portlets deployed in different wars. Or perhaps you can, and I'm missing something fundamentally spectacular about SB. Could you point me towards some piece of technical documentation about this?
The class loader proxying that SB generates is a big part of the value equation for SB over any other ORM package...
When you have two different web apps (i.e. two different portlets deployed as separate wars), the impact of the class loader is little understood (there's lots of posts in the forum that result from not understanding the class loader issues).
The impact is best represented in an example. Say you have a jar, example.jar, and this jar has a class com.example.Class. The exact same jar can be put into the WEB-INF/lib of each of the wars. Even though the class is exactly the same from exactly the same jar, at runtime they are (for all intents and purposes) completely different.
Getting across the class loader boundary is done basically by using reflection. Using reflection, you can invoke methods on any object, even one loaded by a different class loader. The challenge, however, is the passing of data. Simple java primitives (and their object counterparts) can be directly used as they are always loaded by the JVM's class loader and have wide scope. But any java object (a pojo for example), cannot. The instance of Class created in the one web app cannot be passed as-is via reflection to the object that's been loaded in the other web app's class loader.
The only way to cross this kind of boundary is via serialization. SB actually generates code that lives in the service jar to serialize the instance (using java serialization) and pass the serialized version to the other class loader, where it is deserialized and used within the other class loader.
From the SB side, it's actually doing a lot of work. A typical sequence is:
1. Entity e = EntityLocalServiceUtil.createEntity(primaryKey);
2. <populate entity w/ values>.
3. EntityLocalServiceUtil.addEntity(e);
The actual steps behind this are:
1. EntityLocalServiceUtil.createEntity() will serialize the primary key and use a bunch of reflection magic to invoke the createEntity() method defined in the EntityLocalService classes in the service providing plugin.
2. The plugin creates an implementation class and sets the primary key.
3. The implementation is serialized and passed back to the EntityLocalServiceUtil side.
4. The EntityLocalServiceUtil side will deserialize into a locally available instance and is returned.
5. The <populate entity w/ values> sets values in the local instance within the local class loader.
6. The EntityLocalServiceUtil.addEntity() method serializes the local instance, invokes the service side code w/ the serialized version.
7. The service side deserializes the instance, passes to the service side's addEntity() method, result is the updated Entity which is deserialized and handed back.
8. The EntityLocalServiceUtil.addEntity() method deserializes the value given back and returns the instance to the local method.
Serialization is actually one of the magical hidden parts of SB. Every entity, every collection, whatever is passed as an argument or returned as a result, all of these things require serialization to cross the class loader boundary.
So just by leveraging SB and the generated service jar, you get automatic serialization support and a bridge across the class loader boundary.
What I critize is the "builder" part in "service builder".
Well, it is actually a 'builder' and not just a generator, although depending upon usage, it can just seem that way...
Basically it goes something like this... You define an entity in your service.xml file and set the local-service="true" and remote-service="true", then run SB. What you get out of this is:
* A separate service jar that has the shared code for other plugins.
* Support for local access using the XxxLocalServiceUtil classes.
* Support for remote access (web services) to your entity.
* Full support for crossing the class loader boundary to access your entity, plus additional methods that you add to XxxLocalServiceImpl classes.
* Full CRUD support for your entity in the database.
* Automagically creates the DDL to create the database table(s) if they don't exist. Deploy the plugin and your table(s) will be created for you.
* Ability to define custom finders in the entity definition, and SB generates the code to support the finders.
* DynamicQuery access to construct queries in a java DSL without knowing a lick of SQL (although understanding the relational data model is necessary).
So one entity definition gives you all of this functionality, and you really don't have to write a line of java code to get a basic SB project up w/ full CRUD access, web services, and class loader boundary transparency.
The service jar that is built by SB is provided to the plugins that need to access the entities. You have two options for sharing this. The first is to move the service jar to the global lib directory (tomcat's lib/ext directory for example), but this has drawbacks. First, you cannot do a redeployment if the app server is running as the current jar will be open, so a restart is necessary to do a redeploy. Really the only time you should go this route is if you need access to the service layer from a hook or ext plugin (you're going to use the service from within the portal itself). The second option, my preferred option, is to add a "required deployment context" entry in the liferay-plugin-package.properties file for the plugin that will be consuming the service. The Liferay IDE will automagically copy the service jar to the local plugins' docroot/WEB-INF/lib directory (does this every time you rebuild the services within the IDE). Since it's not a global lib deployment, you can still leverage the hot deployment process.
The other portion of the code generated by SB remains in the project hosting the service. Here you primarily will do your work in the XxxLocalServiceImpl files to add methods, expose finders, implement some business logic, etc. When you're done making changes here, you re-run SB and the changes will get propagated to the service jar.
Although the focus of SB is on a DB access layer, you can also use it to share non-db code. Define an entity w/o columns and add your methods to the XxxLocalServiceImpl, and these functions will be available in the service jar, so other plugins can call the methods as though they were local, yet the functions are shared by all plugins in the system.
I know this was a lengthy tirade on SB, but it is necessary info to help explain the value of SB and why you should use it. Hopefully it helps...
Be kell jelentkezni ahhoz, hogy ez helytelenként legyen megjelölve.