Liferay: Líder no Quadrante Mágico da Gartner pelo sexto ano consecutivo! Leia mais aqui
« Voltar para Development

Service Builder

The official documentation for Service Builder is at the Liferay Developer Network: https://dev.liferay.com/develop/tutorials/-/knowledge_base/6-2/service-builder

Introduction #

The Service Builder is a tool built by Liferay to automate the creation of interfaces and classes that are used by a given portal or portlet. This includes code for EJBs, Spring, Persistence, and Model.

The input to the Service Builder is an XML file, typically /ext/ext-ejb/service.xml. For a complete description of the service.xml syntax refer to the well documented DTD at http://www.liferay.com/dtd/liferay-service-builder_6_0_0.dtd

Value Object Model Classes #

Since 4.2, the Service Builder has changed the way value object model classes are generated. Now, interfaces for all model classes are placed into the portal-service.jar. Why would you want to do this? So that the portal-service.jar can be made accessible by WARs that are deployed outside of the classpath of the main code base.

Take for instance the Contact family of classes:

All the classes are generated by Service Builder and should not be modified, with the exception of ContactImpl. Let's look at what each of these classes does.

com.liferay.portal.model.BaseModel #

This interface is the base interface for all model classes found in Liferay. The only methods defined in this interface identify whether or not this value object model is new. Ultimately, the persistent layer uses this method to determine whether to update or add a new entry in the persistence.

com.liferay.portal.model.impl.BaseModelImpl #

This abstract class simply implements BaseModel.

com.liferay.portal.model.ContactModel #

This interface provides a representation of a particular entry in the persistence. For example, there can be one ContactModel representing Joe Bloggs and another representing John Smith.

com.liferay.portal.model.impl.ContactModelImpl #

This class has the implementation of the methods defined in ContactModel. Note that there is no direct connection to one another.

com.liferay.portal.model.Contact #

This interface describes extra helper methods that a developer may find useful when dealing with a given contact. Hence, the developer may want to get the first name or last name of the contact. Note that these entries are not persisted in the Contact_ table but are simply helper methods. This is the value object model that most developers will see -- both returned by the various services and accessible in a separate WAR.

com.liferay.portal.model.impl.ContactImpl #

It is here that the developer actually implements the helper methods found in the Contact interface. What is really happening when a helper method is called?

 public static String getFullName(
         String firstName, String middleName, String lastName) {
     if (Validator.isNull(middleName)) {
         return firstName + StringPool.SPACE + lastName;
     }
     else {
         return firstName + StringPool.SPACE + middleName +
             StringPool.SPACE + lastName;
     }
 }
 public String getFullName() {
     return getFullName(getFirstName(), getMiddleName(), getLastName());
 }

The getFullName() helper method is actually combining the first name, middle name, and last name of a given contact to form a full name.

So, how do you actually get these method signatures defined in the Contact interface if you are never to edit that file? Service Builder. You simply add the new method to the ContactImpl class, run the build-service target for the corresponding service.xml file and the Service Builder will propagate all the needed signatures.

Service Classes #

The class diagram of the MODEL, PERSISTENCE & SERVICE

Persistence Classes #

Create your own service.xml #

The input to the Service Builder is an XML file, typically /ext/ext-ejb/service.xml. For a complete description of the service.xml syntax refer to the well documented DTD at http://www.liferay.com/dtd/liferay-service-builder_6_0_0.dtd

Create a column with no default datatype #

If you look at portal/portal-ejb/classes/com/liferay/portal/service.xml you'll find many examples like this one

<column name="groups" type="Collection" entity="Group" mapping-table="Groups_Orgs" />

from the entity "Organization".

If you'd like to create your own service.xml (e.g. ext/ext-ejb/service.xml) don't reuse this line as it is within your own entity. You'll get this output

     [java] java.lang.ArrayIndexOutOfBoundsException: -1
     [java]     at java.util.ArrayList.get(ArrayList.java:323)
     [java]     at com.liferay.portal.tools.ServiceBuilder.getEntity(ServiceBuilder.java:713)
     [java]     at com.liferay.portal.tools.ServiceBuilder._createPersistence(ServiceBuilder.java:2283)
     [java]     at com.liferay.portal.tools.ServiceBuilder.<init>(ServiceBuilder.java:646)
     [java]     at com.liferay.portal.tools.ServiceBuilder.<init>(ServiceBuilder.java:330)
     [java]     at com.liferay.portal.tools.ServiceBuilder.main(ServiceBuilder.java:86)

while running 'ant service-build' and your entity won't be created. The reason is, that the Service-Builder will look for each entity, that does not contain a '.' within it's name, within your 'service.xml'. The entity Group is specified within 'portal/portal-ejb/classes/com/liferay/portal/service.xml', so you have to set up the full qualified name of 'Group' within the entity attribute.

<column name="groups" type="Collection" entity="com.liferay.portal.Group" mapping-table="Groups_Orgs" />

The full qualified name is not the full qualified name of the generated interface

com.liferay.portal.model.Group

It is the path, where the Service-Builder will look after the service.xml.

com.liferay.portal.Group

will be resolved to relativ path

src/com/liferay/portal/service.xml

The Service-Builder assumes, that within this service.xml, the entity Group is declared. If you get an output like this

     [java] java.lang.NullPointerException
     [java]     at java.io.Reader.<init>(Reader.java:61)
     [java]     at java.io.InputStreamReader.<init>(InputStreamReader.java:55)
     [java]     at com.liferay.util.StringUtil.read(StringUtil.java:320)
     [java]     at com.liferay.util.StringUtil.read(StringUtil.java:316)
     [java]     at com.liferay.portal.tools.ServiceBuilder.getEntity(ServiceBuilder.java:731)
     [java]     at com.liferay.portal.tools.ServiceBuilder._createPersistence(ServiceBuilder.java:2283)
     [java]     at com.liferay.portal.tools.ServiceBuilder.<init>(ServiceBuilder.java:646)
     [java]     at com.liferay.portal.tools.ServiceBuilder.<init>(ServiceBuilder.java:330)
     [java]     at com.liferay.portal.tools.ServiceBuilder.main(ServiceBuilder.java:86)

while running ant build-service, the service.xml could not be found in the path you specified.

Permissions Filtering #

Finder methods are created automatically but these will return all the results. FilterFindBy method can also be generated under certain conditions and they will return the results filtered by permissions. The conditions are:

  1. the entity has a simple primitive pk
  2. the entity has permission checke registered in resource-actions/....xml
  3. the entity has user id an group id fields
  4. the finder method has group id in it

See Also #

0 Anexos
183000 Visualizações
Média (8 Votos)
A média da avaliação é 2.25 estrelas de 5.
Comentários
Respostas do tópico Autor Data
i am getting this error. it do create class... Adeela Huma 19 de Março de 2009 00:20
You are using some keywords in your service.xml... petar banicevic 14 de Julho de 2009 18:00
I am getting this error..when i am doing... Yogesh Prabhu 18 de Março de 2010 21:56
The error is as follows:... Yogesh Prabhu 18 de Março de 2010 22:02
Are there any changes in the service builder... Gaurang G 17 de Setembro de 2010 00:32
There are two aditions in the dtd for LR600.DTD... Antonio Ivars 14 de Outubro de 2010 00:25
"filter-primary" is _NONSENCE_ I has been... Fuad Efendi 17 de Abril de 2015 11:29
Is there any "reverse engineering" way? I mean,... Puj Z 22 de Outubro de 2010 04:39
Wow, great entry, thanks a lot!!! I have a... Luis Rodríguez Fernández 24 de Novembro de 2010 00:17
Hi liferay, You guys are applying some logic on... amit mahajan 21 de Setembro de 2011 00:55
#Amit: There is a lot you can do with... Apurv Chandra 24 de Novembro de 2011 09:01
How about defining the column lengths? 75... Mikko Torri 13 de Dezembro de 2011 06:36
You have to do this in... Dirk Ulrich 5 de Janeiro de 2012 03:03
Hello, i am getting this error following the... alex marroquin 8 de Março de 2012 23:28

i am getting this error. it do create class files but they are empty..

07:17:52,513 ERROR [java:355] PARSER_ERROR
E:\liferay\ext\ext-impl\ServiceBuilder.temp:38:16: unexpected token: com
at de.hunsicker.jalopy.language.antlr.InternalJavaParser.typeDefinitionInternal(Int­ernalJavaParser.java:660)
at de.hunsicker.jalopy.language.antlr.InternalJavaParser.typeDefinition(InternalJav­aParser.java:465)
at de.hunsicker.jalopy.language.antlr.InternalJavaParser.parse(InternalJavaParser.j­ava:308)
at de.hunsicker.jalopy.language.JavaRecognizer.parse(JavaRecognizer.java:588)
at de.hunsicker.jalopy.Jalopy.parse(Jalopy.java:1212)
at de.hunsicker.jalopy.Jalopy.format(Jalopy.java:1044)
at de.hunsicker.jalopy.Jalopy.format(Jalopy.java:1017)
at de.hunsicker.jalopy.language.antlr.InternalJavaParser.parse(InternalJavaParser.j­ava:296)
at de.hunsicker.jalopy.language.JavaRecognizer.parse(JavaRecognizer.java:588)
at de.hunsicker.jalopy.Jalopy.parse(Jalopy.java:1212)
at de.hunsicker.jalopy.Jalopy.format(Jalopy.java:1044)
at de.hunsicker.jalopy.Jalopy.format(Jalopy.java:1017)
at com.liferay.portal.tools.servicebuilder.ServiceBuilder.writeFile(ServiceBuilder.­java:317)
at com.liferay.portal.tools.servicebuilder.ServiceBuilder._createModel(ServiceBuild­er.java:1882)
at com.liferay.portal.tools.servicebuilder.ServiceBuilder.<init>(ServiceBuilder.jav­a:932)
at com.liferay.portal.tools.servicebuilder.ServiceBuilder.<init>(ServiceBuilder.jav­a:393)
at com.liferay.portal.tools.servicebuilder.ServiceBuilder.main(ServiceBuilder.java:­159)
at com.liferay.portal.tools.servicebuilder.ServiceBuilder.writeFile(ServiceBuilder.­java:317)
at com.liferay.portal.tools.servicebuilder.ServiceBuilder._createModel(ServiceBuild­er.java:1882)
at com.liferay.portal.tools.servicebuilder.ServiceBuilder.<init>(ServiceBuilder.jav­a:932)
at com.liferay.portal.tools.servicebuilder.ServiceBuilder.<init>(ServiceBuilder.jav­a:393)
at com.liferay.portal.tools.servicebuilder.ServiceBuilder.main(ServiceBuilder.java:­159)
07:17:52,513 ERROR [io:1071] UNKNOWN_ERROR
expecting EOF, found '}'
Postado em 19/03/09 00:20.
You are using some keywords in your service.xml that are reserved. Please try to avoid java keywords as column names such as "new" etc,
Postado em 14/07/09 18:00 em resposta a Adeela Huma.
I am getting this error..when i am doing build-service..
Can u please provide a solution..
I am not able to understand what the error is?
Is it a problem with the Portal Source Code..
Postado em 18/03/10 21:56.
The error is as follows:
http://www.liferay.com/community/forums/-/message_boards/message/4737773­..

On this page there is a error-db.doc file..
That contains a screenshot of the error..
help me with a solution please..
Postado em 18/03/10 22:02 em resposta a Yogesh Prabhu.
Are there any changes in the service builder for Liferay 6?
Postado em 17/09/10 00:32.
There are two aditions in the dtd for LR600.DTD

It has been added a "filter-primary CDATA #IMPLIED" for the create sequence SQL statement (Line 275)

Copied from the DTD:
"The filter-primary value specifies the column to use as the primary key column when using filter finders. Only one column should ever have this value set to true. If no column has this set to true, then the default primary column be used."

It has been added as well a "arrayable-operator CDATA #IMPLIED" for the finder query. (Line 408)

Copied from the DTD:
"The attribute arrayable-operator takes in the values AND or OR and will generate an additional finder where this column's parameter takes an array instead of a single value. Every value in this array will be compared with the column using the comparator, and the conditions will be combined with either an AND or OR operator. For example, a finder column with the = comparator and an arrayable-operator of OR will act like an IN clause."


Related with the Service Builder's usage, I guess it's still the same, but I cannot swear it... yet.
Postado em 14/10/10 00:25 em resposta a Gaurang G.
Is there any "reverse engineering" way? I mean, when you already have some tables and you want to generate the service.xml file.
Helps would be appreciated! emoticon
Postado em 22/10/10 04:39.
Wow, great entry, thanks a lot!!!

I have a doubt. I have tried to build the ReportsEntry service builder that it comes with the liferay-portal-ext-5.2.3. It seems to work fine, I can see the services, the impls, and I can also deploy the reports portlet, the ext-impl.jar and the ext-service.jar. As I can see the service builder has also created the hibernate mappings for me, so is it not suppose that it also should create the database entity ReportsEntry?

I think I'm missing something...

Thanks a lot,

Luis
Postado em 24/11/10 00:17.
Hi liferay,
You guys are applying some logic on a xml file and generating code, why not to make that logic available in xml file itself and allow it to be configurable.

How about giving control for changing package strutcture, generating more code, all this will be very useful, kindly follow spring like style and add this feature. (One more control xml to control/configure the generated code.)
Postado em 21/09/11 00:55 em resposta a Luis Rodríguez Fernández.
#Amit: There is a lot you can do with service.xml read the documented DTD you will be able to understand.
Postado em 24/11/11 09:01 em resposta a amit mahajan.
How about defining the column lengths?

75 chars for a string isn't a lot.
Postado em 13/12/11 06:36 em resposta a Apurv Chandra.
You have to do this in src/META-INF/portlet-model-hints.xml:
<model name="your.EntityClass">
<field name="language" type="String">
<hint name="max-length">3</hint>
</field>
</model>
Postado em 05/01/12 03:03 em resposta a Mikko Torri.
Hello, i am getting this error following the liferay in action book bye Richard Sezov, in chapter 3.

C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-registration-portlet>ant bu
ild-service
Buildfile: C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-registration-por
tlet\build.­xml

build-service:
Copying 1 file to C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\produ
ct-registration-portlet\docroo­t\WEB-INF\classes
Loading jar:file:/C:/liferay-portal-6.1.0-ce-ga1/tomcat-7.0.23/webap
ps/ROOT/WEB-INF/lib/­portal-impl.jar!/system.properties
mar 08, 2012 10:25:08 PM com.liferay.portal.kernel.log.Jdk14LogImpl
info
Informaci¾n: Global lib directory /C:/liferay-portal-6.1.0-ce-ga1/to
mcat-7.0.23/lib/ext/
mar 08, 2012 10:25:08 PM com.liferay.portal.kernel.log.Jdk14LogImpl
info
Informaci¾n: Portal lib directory /C:/liferay-portal-6.1.0-ce-ga1/to
mcat-7.0.23/webapps/ROOT/WEB-INF/lib/
22:25:08,436 INFO [EasyConf:122] Refreshed the configuration of all
components
22:25:08,990 INFO [ConfigurationLoader:56] Properties for jar:file:
/C:/liferay-portal-6.1.0-ce-ga1/tomcat-7.0.23/webapps/ROOT/WEB-INF/lib/­portal-im
pl.jar!/portal loaded from [jar:file:/C:/liferay-portal-6.1.0-ce-ga1/tomcat-7.0.
23/webapps/ROOT/WEB-INF/lib­/portal-impl.jar!/com/liferay/portal/tools/dependenci
es/portal-tools.properties,­ jar:file:/C:/liferay-portal-6.1.0-ce-ga1/tomcat-7.0.
23/webapps/ROOT/WEB-INF/lib/­portal-impl.jar!/portal.properties]
Loading jar:file:/C:/liferay-portal-6.1.0-ce-ga1/tomcat-7.0.23/webap
ps/ROOT/WEB-INF/lib/­portal-impl.jar!/portal.properties
Loading jar:file:/C:/liferay-portal-6.1.0-ce-ga1/tomcat-7.0.23/webap
ps/ROOT/WEB-INF/lib/­portal-impl.jar!/com/liferay/portal/tools/dependencies/porta
l-tools.properties
Building PRProduct
Deleting: C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-regis
tration-portlet\Servic­eBuilder.temp

compile-java:
Compiling 4 source files to C:\liferay-plugins-sdk-6.1.0-ce-ga1\port
lets\product-registration-portlet\docroo­t\WEB-INF\service-classes
----------
1. ERROR in C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-reg
istration-portlet\docroo­t\WEB-INF\service\com\inkwell\internet\productregistrati
on\service\PRProductLoca­lService.java (at line 239)
public PRProduct addProduct(PRProduct newProduct, long userId)
^^^^^^^^^
PRProduct cannot be resolved to a type
----------
2. ERROR in C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-reg
istration-portlet\docroo­t\WEB-INF\service\com\inkwell\internet\productregistrati
on\service\PRProductLoca­lService.java (at line 239)
public PRProduct addProduct(PRProduct newProduct, long userId)
^^^^^^^^^
PRProduct cannot be resolved to a type
----------
----------
3. ERROR in C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-reg
istration-portlet\docroo­t\WEB-INF\service\com\inkwell\internet\productregistrati
on\service\PRProductLoca­lServiceClp.java (at line 24)
public class PRProductLocalServiceClp implements PRProductLocalS
ervice {
^^^^^^^^^^^^^^^^^^^^^^^^
The type PRProductLocalServiceClp must implement the inherited abstr
act method PRProductLocalService.addProduct(PRProduct, long)
----------
4. ERROR in C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-reg
istration-portlet\docroo­t\WEB-INF\service\com\inkwell\internet\productregistrati
on\service\PRProductLoca­lServiceClp.java (at line 92)
"addProduct", PRProduct.class, long.class);
^^^^^^^^^
PRProduct cannot be resolved to a type
----------
5. ERROR in C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-reg
istration-portlet\docroo­t\WEB-INF\service\com\inkwell\internet\productregistrati
on\service\PRProductLoca­lServiceClp.java (at line 560)
public PRProduct addProduct(PRProduct newProduct, long userId)
^^^^^^^^^
PRProduct cannot be resolved to a type
----------
6. ERROR in C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-reg
istration-portlet\docroo­t\WEB-INF\service\com\inkwell\internet\productregistrati
on\service\PRProductLoca­lServiceClp.java (at line 560)
public PRProduct addProduct(PRProduct newProduct, long userId)
^^^^^^^^^
PRProduct cannot be resolved to a type
----------
7. ERROR in C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-reg
istration-portlet\docroo­t\WEB-INF\service\com\inkwell\internet\productregistrati
on\service\PRProductLoca­lServiceClp.java (at line 589)
return (PRProduct)ClpSerializer.translateOutput(returnObj);
^^^^^^^^^
PRProduct cannot be resolved to a type
----------
----------
8. ERROR in C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-reg
istration-portlet\docroo­t\WEB-INF\service\com\inkwell\internet\productregistrati
on\service\PRProductLoca­lServiceUtil.java (at line 265)
public static PRProduct addProduct(PRProduct newProduct, long us
erId)
^^^^^^^^^
PRProduct cannot be resolved to a type
----------
9. ERROR in C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-reg
istration-portlet\docroo­t\WEB-INF\service\com\inkwell\internet\productregistrati
on\service\PRProductLoca­lServiceUtil.java (at line 265)
public static PRProduct addProduct(PRProduct newProduct, long us
erId)
^^^^^^^^^
PRProduct cannot be resolved to a type
----------
----------
10. ERROR in C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-re
gistration-portlet\docroo­t\WEB-INF\service\com\inkwell\internet\productregistrat
ion\service\PRProductLoca­lServiceWrapper.java (at line 28)
public class PRProductLocalServiceWrapper implements PRProductLo
calService,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The type PRProductLocalServiceWrapper must implement the inherited a
bstract method PRProductLocalService.addProduct(PRProduct, long)
----------
11. ERROR in C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-re
gistration-portlet\docroo­t\WEB-INF\service\com\inkwell\internet\productregistrat
ion\service\PRProductLoca­lServiceWrapper.java (at line 258)
public PRProduct addProduct(PRProduct newProduct, long userId)
^^^^^^^^^
PRProduct cannot be resolved to a type
----------
12. ERROR in C:\liferay-plugins-sdk-6.1.0-ce-ga1\portlets\product-re
gistration-portlet\docroo­t\WEB-INF\service\com\inkwell\internet\productregistrat
ion\service\PRProductLoca­lServiceWrapper.java (at line 258)
public PRProduct addProduct(PRProduct newProduct, long userId)
^^^^^^^^^
PRProduct cannot be resolved to a type
----------
12 problems (12 errors)

BUILD FAILED
C:\liferay-plugins-sdk-6.1.0-ce-ga1\build-common-plugin.xml:212: The following e
rror occurred while executing this line:
C:\liferay-plugins-sdk-6.1.0-ce-ga1\build-common.xml:94: Compile failed; see the
compiler error output for details.

Total time: 13 seconds

HELP PLEASE
Postado em 08/03/12 23:28.
"filter-primary" is _NONSENCE_

I has been added as part of https://issues.liferay.com/browse/LPS-10750 for the version 6.0.3 GA

As of today, "master" branch, Liferay 7.0.0 M4 and later, Service Builder reads this value and generates method "EntityColumn.getFilterPKColumn()" for the entity. However, this method is never being read by any of the ServiceBuilder generated class.

LPS-10750 mentions MBMessage as a sample of the use case for "filter-primary". However, there are no any "automated" uses of that. Quote from LPS-10750: "See messageboards/service.xml for how it's used to create filterFinders that use rootMessageId instead of the default primary key messageId for determining which MBMessage entries to show via inline SQL."

I didn't find any usage of that in the current (April 17, 2015) codebase.

We also have "resourcePrimaryKey" additionally to "primaryKey"; for instance, in case of a Journal Article it is used to group under same ID the same article (version control, multi-language support); and in some other places we have "resourcePrimaryKey" used for permissioning (such as Knowledge Base portlet, although we can easily use existing Id instead)

Perhaps "filter-primary" is a nice story, but it is _UNFINISHED_ (we need to reopen LPS-10750)

Service Builder does not use it; documentation is not full; no any samples provided.
Postado em 17/04/15 11:29 em resposta a Antonio Ivars.