1. service.xml

Liferay Portal features a convenient code generation module that allows the developer to auto generate all of the "Model" components for a portlet. The business and persistance tier classes are auto generated based on specifications outlined in a file called service.xml. Each portlet can have its own service.xml where the portlet and each entity in the portlet are defined. A portlet entity is defined as a table specifying primary keys, audit fields, and other fields used in the table as well as any finder methods that will be required. Custom portlet exceptions can also be auto generated.

Below is an example service.xml file for the Bookmarks Portlet.

<?xml version="1.0"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 4.0.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_4_0_0.dtd">

<service-builder root-dir=".." package-path="com.liferay.portlet">
	<portlet name="Bookmarks" short-name="Bookmarks" />
	<entity name="BookmarksEntry" local-service="true" remote-service="true">

		<!-- PK fields -->

		<column name="entryId" type="String" primary="true" />

		<!-- Audit fields -->

		<column name="companyId" type="String" />
		<column name="userId" type="String" />
		<column name="createDate" type="Date" />
		<column name="modifiedDate" type="Date" />

		<!-- Other fields -->

		<column name="folderId" type="String" />
		<column name="name" type="String" />
		<column name="url" type="String" />
		<column name="comments" type="String" />
		<column name="visits" type="int" />

		<!-- Order -->

		<order by="asc">
			<order-column name="folderId" />
			<order-column name="name" case-sensitive="false" />
		</order>

		<!-- Finder methods -->

		<finder name="FolderId" return-type="Collection">
			<finder-column name="folderId" />
		</finder>

		<!-- References -->

		<reference package-path="com.liferay.portal" entity="Resource" />
	</entity>
	<entity name="BookmarksFolder" local-service="true" remote-service="true">

		<!-- PK fields -->

		<column name="folderId" type="String" primary="true" />

		<!-- Group instance -->

		<column name="groupId" type="String" />

		<!-- Audit fields -->

		<column name="companyId" type="String" />
		<column name="userId" type="String" />
		<column name="createDate" type="Date" />
		<column name="modifiedDate" type="Date" />

		<!-- Other fields -->

		<column name="parentFolderId" type="String" />
		<column name="name" type="String" />
		<column name="description" type="String" />

		<!-- Order -->

		<order by="asc">
			<order-column name="parentFolderId" />
			<order-column name="name" case-sensitive="false" />
		</order>

		<!-- Finder methods -->

		<finder name="GroupId" return-type="Collection">
			<finder-column name="groupId" />
		</finder>
		<finder name="G_P" return-type="Collection">
			<finder-column name="groupId" />
			<finder-column name="parentFolderId" />
		</finder>

		<!-- References -->

		<reference package-path="com.liferay.portal" entity="Resource" />
	</entity>
	<exceptions>
		<exception>EntryURL</exception>
		<exception>FolderName</exception>
	</exceptions>
</service-builder>
  

Upon reading service.xml found in the Bookmarks portlet, the following model classes are generated. Each model class reflects a table in the database. Never edit BookmarksEntryModel. Do edit BookmarksEntry to add hand massaged code. BookmarksEntry is generated once and extends BookmarksEntryModel. This allows the ease of generated code and flexibility of hand massaged code.

Most of the EJBs, HBMs, and Models are generated through the ant task build-service, which reads the file service.xml in /portal-ejb. Each portlet that persist data has its own service.xml (do a search in /portal-ejb and you will get a list back). Copy this file to /portal-ejb when you want to generate the persistence classes for that portlet. This is an internal tool that is built on top of the XDoclet engine.

For example, upon reading service.xml found in the Bookmarks portlet, the following model classes are generated. Each model class reflects a table in the database. Never edit BookmarksEntryModel. Do edit BookmarksEntry to add hand massaged code. BookmarksEntry is generated once and extends BookmarksEntryModel. This allows you the ease of generated code and flexibility of hand massaged code.

com.liferay.portlet.bookmarks.model.BookmarksEntry

com.liferay.portlet.bookmarks.model.BookmarksEntryModel

com.liferay.portlet.bookmarks.model.BookmarksFolder

com.liferay.portlet.bookmarks.model.BookmarksFolderModel

Hibernate classes are generated that map to the model classes. This allows for an n-tier architecture for cases where your model classes are marshalled across the wire and your Hibernate classes are not.

com.liferay.portlet.bookmarks.service.persistence.BookmarksEntryHBM

com.liferay.portlet.bookmarks.service.persistence.BookmarksFolderHBM

Persistence methods to add, update, delete, find, remove, and count the Hibernate entries are generated as the default persistence mechanism.

com.liferay.portlet.bookmarks.service.persistence.BookmarksEntryPersistence

com.liferay.portlet.bookmarks.service.persistence.BookmarksFolderPersistence

Helper classes are generated that call the persistence methods. By default, the helper classes call the Hibernate persistence methods to update the database. You can override this in portal.properties and set your own persistence class as long as it extends the default persistence class. This means you can customize where you store your data. It can be a traditional database, a LDAP server, or even something else.

com.liferay.portlet.bookmarks.service.persistence.BookmarksEntryUtil

com.liferay.portlet.bookmarks.service.persistence.BookmarksFolderUtil

Pooling classes are also created to minimize object creation. Behavior can be modified in portal.properties.

com.liferay.portlet.bookmarks.service.persistence.BookmarksEntryPool

com.liferay.portlet.bookmarks.service.persistence.BookmarksFolderPool

POJO implementations that extend PrincipalBean are generated to hold business logic that check the caller principal and can be called remotely. Calling getUserId() returns the user id of the current user. Calling getUser() returns the User model that represents the current user. The Session EJB that extends the POJO implementation implements PrincipalSessionBean.

For example, these classes allow you to delete a bookmark entry or folder only if you are the creator of that entry or folder.

These classes are only generated once if they do not already exist.

com.liferay.portlet.bookmarks.service.impl.BookmarksEntryServiceImpl

com.liferay.portlet.bookmarks.service.impl.BookmarksFolderServiceImpl

Helper classes are generated based on the POJO implementations. They help save developer time and prevent polluted code. Instead of writing many lines of code just to look up the appropriate Session EJB wrapper or POJO implementation, you simply call BookmarksEntryServiceUtil.addEntry to call the equivalent method in BookmarksEntryServiceImpl.addEntry.

BookmarksEntryServiceUtil calls BookmarksFolderServiceFactory to look up the class that implements BookmarksEntryService. BookmarksFolderServiceFactory defers to Spring and settings in portal.properties on whether to load the Session EJB wrapper or the plain POJO implementation. The Session EJB extends the POJO implementation.

com.liferay.portlet.bookmarks.service.ejb.BookmarksEntryServiceEJB

com.liferay.portlet.bookmarks.service.ejb.BookmarksEntryServiceEJBImpl

com.liferay.portlet.bookmarks.service.ejb.BookmarksEntryServiceHome

com.liferay.portlet.bookmarks.service.spring.BookmarksEntryService

com.liferay.portlet.bookmarks.service.spring.BookmarksEntryServiceFactory

com.liferay.portlet.bookmarks.service.spring.BookmarksEntryServiceUtil

com.liferay.portlet.bookmarks.service.ejb.BookmarksFolderServiceEJB

com.liferay.portlet.bookmarks.service.ejb.BookmarksFolderServiceEJBImpl

com.liferay.portlet.bookmarks.service.ejb.BookmarksFolderServiceHome

com.liferay.portlet.bookmarks.seervice.spring.BookmarksFolderService

com.liferay.portlet.bookmarks.service.spring.BookmarksFolderServiceFactory

com.liferay.portlet.bookmarks.service.spring.BookmarksFolderServiceUtil

Tunneling classes are generated so that developers can call the POJO implementations over port 80. An example of this is given in section V of this document.

com.liferay.portlet.bookmarks.service.http.BookmarksEntryServiceHttp

com.liferay.portlet.bookmarks.service.http.BookmarksFolderServiceHttp

Soap classes are generated so that developers can call the POJO implementations over port 80. Soap is slower than tunneling because tunneling streams request in binary format. Soap is more flexible than tunneling because the client classes are not limited to Java.

com.liferay.portlet.bookmarks.service.http.BookmarksEntryServiceSoap

com.liferay.portlet.bookmarks.service.http.BookmarksFolderServiceSoap

POJO implementations classes that do not extend PrincipalBean are generated to hold business logic that do not check the caller principal and can be called locally. These classes exist so that business logic can be easily integrated with other projects.

These classes are only generated once if they do not already exist.

com.liferay.portlet.bookmarks.service.impl.BookmarksEntryLocalServiceImpl

com.liferay.portlet.bookmarks.service.impl.BookmarksFolderLocalServiceImpl

Helper classes are also generated.

com.liferay.portlet.bookmarks.service.ejb.BookmarksEntryLocalServiceEJB

com.liferay.portlet.bookmarks.service.ejb.BookmarksEntryLocalServiceEJBImpl

com.liferay.portlet.bookmarks.service.ejb.BookmarksEntryLocalServiceHome

com.liferay.portlet.bookmarks.service.spring.BookmarksEntryLocalService

com.liferay.portlet.bookmarks.service.spring.BookmarksEntryLocalServiceFactory

com.liferay.portlet.bookmarks.service.spring.BookmarksEntryLocalServiceUtil

com.liferay.portlet.bookmarks.service.ejb.BookmarksFolderLocalServiceEJB

com.liferay.portlet.bookmarks.service.ejb.BookmarksFolderLocalServiceEJBImpl

com.liferay.portlet.bookmarks.service.ejb.BookmarksFolderLocalServiceHome

com.liferay.portlet.bookmarks.service.spring.BookmarksFolderLocalService

com.liferay.portlet.bookmarks.service.spring.BookmarksFolderLocalServiceFactory

com.liferay.portlet.bookmarks.service.spring.BookmarksFolderLocalServiceUtil

Some of our users needed to call the Local Service classes remotely, so Remote Service classes that parallel their Local counterparts are also generated.

com.liferay.portlet.bookmarks.service.ejb.BookmarksEntryRemoteServiceEJB

com.liferay.portlet.bookmarks.service.ejb.BookmarksEntryRemoteServiceEJBImpl

com.liferay.portlet.bookmarks.service.ejb.BookmarksEntryRemoteServiceHome

com.liferay.portlet.bookmarks.service.spring.BookmarksEntryRemoteService

com.liferay.portlet.bookmarks.service.spring.BookmarksEntryRemoteServiceFactory

com.liferay.portlet.bookmarks.service.spring.BookmarksEntryRemoteServiceUtil

com.liferay.portlet.bookmarks.service.ejb.BookmarksFolderRemoteServiceEJB

com.liferay.portlet.bookmarks.service.ejb.BookmarksFolderRemoteServiceEJBImpl

com.liferay.portlet.bookmarks.service.ejb.BookmarksFolderRemoteServiceHome

com.liferay.portlet.bookmarks.service.spring.BookmarksFolderRemoteService

com.liferay.portlet.bookmarks.service.spring.BookmarksFolderRemoteServiceFactory

com.liferay.portlet.bookmarks.service.spring.BookmarksFolderRemoteServiceUtil