This post will demonstrate how liferay-maven-sdk can be employed to build a Liferay portlet using Liferay's Service Builder feature. For this purpose we will create service-builder-portlet which is capable of displaying a list of players and adding a new player to this list. The model, persistence layer and data access services will be generated by Service Builder.
But first things first. Download and install liferay-maven-sdk if you haven't done so already (have a look at "Download and Install" page for instructions). Once liferay-maven-sdk is installed in your local repository, you can create the portlet.
So enter the folder (or create one) where you keep your portlets and execute
mvn archetype:generate
you should see a list of available archetypes starting with
Choose archetype:
1: local -> liferay-portlet-archetype (Liferay portlet archetype)
2: local -> liferay-theme-archetype (Liferay theme archetype)
3: internal -> appfuse-basic-jsf (AppFuse archetype for creating a web application with Hibernate, Spring and JSF)
4: internal -> appfuse-basic-spring (AppFuse archetype for creating a web application with Hibernate, Spring and Spring MVC)
...
Choose a number: (1/2/3/4/...) :1
Type 1 and press enter to choose liferay-portlet-archetype. Then provide groupId, artifactID, package and version. For example:
Define value for groupId: : com.commsen.liferay.examples.portlet.servicebuilder
Define value for artifactId: : service-builder-portlet
Define value for version: 1.0-SNAPSHOT: : 1.0
Define value for package: com.commsen.liferay.examples.portlet.servicebuilder: :
Additionally you may execute
mvn eclipse:eclipse
to setup Eclipse IDE if, that is what you are using! At this time your portlet skeleton is ready and you may compile it and even create WAR, but it of course does nothig special.
Let's now add data model and services. Create file service-builder-portlet/src/main/webapp/WEB-INF/service.xml :
<?xml version="1.0"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 5.2.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_5_2_0.dtd">
<service-builder package-path="com.commsen.liferay.examples.portlet.servicebuilder">
<namespace>SB</namespace>
<entity name="Player" local-service="true" remote-service="true">
<!-- PK fields -->
<column name="playerId" type="long" primary="true" />
<!-- Other fields -->
<column name="name" type="String" />
<column name="active" type="boolean" />
<column name="score" type="int" />
<column name="birthday" type="Date" />
<column name="description" type="String" />
<!-- Order -->
<order by="asc">
<order-column name="name" />
</order>
<!-- Finder methods -->
<finder name="ActivePlayers" return-type="Collection">
<finder-column name="active" />
</finder>
</entity>
</service-builder>
The portlet projects created by liferay-portlet-archetype contain maven profile with lifray-maven-plugin’s build-service goal attached to generate-sources phase. To run ServiceBuilder you need to activate the profile:
mvn -P build-service package
If you read carefully the messages you will realize that service and persistence classes were generated in service-builder-portlet/src/main/java-service-api/ folder. Also there should be a few implementation files in service-builder-portlet/src/main/java/.
Now we'll add custom methods. Open the following file service-builder-portlet/src/main/java/com/commsen/liferay/examples/portlet/servicebuilder/service/impl/PlayerLocalServiceImpl.java and paste this code:
public void addPlayer(String name, boolean active, int score, Date birthday, String desc) throws PortalException, SystemException {
long playerId = CounterLocalServiceUtil.increment();
Player player = PlayerUtil.create(playerId);
player.setName(name);
player.setActive(active);
player.setScore(score);
player.setBirthday(birthday);
player.setDescription(desc);
PlayerUtil.update(player, false);
}
public List<Player> getAllPlayers() throws PortalException, SystemException {
return PlayerUtil.findAll();
}
We added methods to the implementation class. The next time we compile the code we need to activate the build-service profile again in order for Service Builder to react on the change and regenerate the API and interfaces. If you want to try it now simply execute
mvn -P build-service compile
Now we can start using these services in our portlet. Open service-builder-portlet/src/main/java/com/commsen/liferay/examples/portlet/servicebuilder/JSPPortlet.java and add this method
@ProcessAction(name = Constants.ADD)
public void addPlayer(ActionRequest actionRequest, ActionResponse actionResponse) throws PortletException, IOException {
String name = ParamUtil.getString(actionRequest, "name");
boolean active = ParamUtil.getBoolean(actionRequest, "active");
int score = ParamUtil.getInteger(actionRequest, "score");
String description = ParamUtil.getString(actionRequest, "description");
int year = ParamUtil.getInteger(actionRequest, "birthday_year");
int month = ParamUtil.getInteger(actionRequest, "birthday_month");
int day = ParamUtil.getInteger(actionRequest, "birthday_day");
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
try {
PlayerLocalServiceUtil.addPlayer(name, active, score, calendar.getTime(), description);
} catch (Exception e) {
throw new PortletException("Failed to add player", e);
}
}
This method handles adding players to database by calling the method we created earliar. To complete the portlet we only need the JSP page which displays the list and render HTML form to add users. The page source code is available here.
That's all! We can now create WAR file (mvn package) deploy it and add some players to our database! For more details have a look at portlet's source code available in examples/service-builder-portlet folder of liferay-maven-sdk.

I follow the steps you described and when i do 'mvn compile', here is what i get:
Building Player
[INFO] [resources:resources {execution: default-resources}]
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 8 resources
[INFO] [compiler:compile {execution: default-compile}]
[INFO] Compiling 16 source files to /Users/sulman/Documents/workspace-portlets/portlet1/service-builder-portlet/target/classes
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure
/Users/sulman/Documents/workspace-portlets/portlet1/service-builder-portlet/src/main/java/com/commsen/liferay/examples/portlet/servicebuilder/service/impl/PlayerServiceImpl.java:[10,105] cannot find symbol
symbol : class PortalException
location: class com.commsen.liferay.examples.portlet.servicebuilder.service.impl.PlayerServiceImpl
/Users/sulman/Documents/workspace-portlets/portlet1/service-builder-portlet/src/main/java/com/commsen/liferay/examples/portlet/servicebuilder/service/impl/PlayerServiceImpl.java:[10,122] cannot find symbol
symbol : class SystemException
location: class com.commsen.liferay.examples.portlet.servicebuilder.service.impl.PlayerServiceImpl
/Users/sulman/Documents/workspace-portlets/portlet1/service-builder-portlet/src/main/java/com/commsen/liferay/examples/portlet/servicebuilder/service/impl/PlayerServiceImpl.java:[26,47] cannot find symbol
symbol : class PortalException
location: class com.commsen.liferay.examples.portlet.servicebuilder.service.impl.PlayerServiceImpl
/Users/sulman/Documents/workspace-portlets/portlet1/service-builder-portlet/src/main/java/com/commsen/liferay/examples/portlet/servicebuilder/service/impl/PlayerServiceImpl.java:[26,64] cannot find symbol
symbol : class SystemException
location: class com.commsen.liferay.examples.portlet.servicebuilder.service.impl.PlayerServiceImpl
/Users/sulman/Documents/workspace-portlets/portlet1/service-builder-portlet/src/main/java-service-api/com/commsen/liferay/examples/portlet/servicebuilder/service/PlayerService.java:[43,26] cannot find symbol
symbol : class Player
location: interface com.commsen.liferay.examples.portlet.servicebuilder.service.PlayerService
/Users/sulman/Documents/workspace-portlets/portlet1/service-builder-portlet/src/main/java-service-api/com/commsen/liferay/examples/portlet/servicebuilder/service/PlayerServiceUtil.java:[38,33] cannot find symbol
symbol : class Player
location: class com.commsen.liferay.examples.portlet.servicebuilder.service.PlayerServiceUtil
/Users/sulman/Documents/workspace-portlets/portlet1/service-builder-portlet/src/main/java-service-api/com/commsen/liferay/examples/portlet/servicebuilder/service/PlayerServiceClp.java:[65,26] cannot find symbol
symbol : class Player
location: class com.commsen.liferay.examples.portlet.servicebuilder.service.PlayerServiceClp
/Users/sulman/Documents/workspace-portlets/portlet1/service-builder-portlet/src/main/java/com/commsen/liferay/examples/portlet/servicebuilder/service/impl/PlayerServiceImpl.java:[14,24] cannot find symbol
symbol : variable PlayerUtil
location: class com.commsen.liferay.examples.portlet.servicebuilder.service.impl.PlayerServiceImpl
/Users/sulman/Documents/workspace-portlets/portlet1/service-builder-portlet/src/main/java/com/commsen/liferay/examples/portlet/servicebuilder/service/impl/PlayerServiceImpl.java:[22,8] cannot find symbol
symbol : variable PlayerUtil
location: class com.commsen.liferay.examples.portlet.servicebuilder.service.impl.PlayerServiceImpl
/Users/sulman/Documents/workspace-portlets/portlet1/service-builder-portlet/src/main/java/com/commsen/liferay/examples/portlet/servicebuilder/service/impl/PlayerServiceImpl.java:[27,15] cannot find symbol
symbol : variable PlayerUtil
location: class com.commsen.liferay.examples.portlet.servicebuilder.service.impl.PlayerServiceImpl
/Users/sulman/Documents/workspace-portlets/portlet1/service-builder-portlet/src/main/java-service-api/com/commsen/liferay/examples/portlet/servicebuilder/service/PlayerServiceClp.java:[89,31] cannot find symbol
symbol : class Player
location: class com.commsen.liferay.examples.portlet.servicebuilder.service.PlayerServiceClp
[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 15 seconds
[INFO] Finished at: Wed Oct 28 13:07:49 GMT 2009
[INFO] Final Memory: 20M/36M
[INFO] ------------------------------------------------------------------------
Any idea whats going on? Am i missing something here?
Sulman.
Hi Sulman, there was a typo in the article about where to place the custom method. It is not PlayerServiceImpl.java as it used to be but PlayerLocalServiceImpl.java
Thank you for reporting this and I hope this will solve your problem.
When using your archetype I get alot of lines like those below even though I have the artifacts in my local .m2/repository/. Is there a way to make that stop?
Downloading: http://repo1.maven.org/maven2/com/commsen/liferay/sdk/libraries/mail/5.2.3/mail-5.2.3.pom
[INFO] Unable to find resource 'com.commsen.liferay.sdk.libraries:mail:pom:5.2.3' in repository central (http://repo1.maven.org/maven2)
It's because the artifacts have no poms that the dependency checking keeps happening.
Looks like you need generatePom=true in all the install file goals
<execution>
<id>install ant</id>
<configuration>
<file>lib/ant.jar</file>
<groupId>com.commsen.liferay.portal.libraries</groupId>
<artifactId>ant</artifactId>
<version>5.2.3</version>
<packaging>jar</packaging>
<generatePom>true</generatePom>
</configuration>
<phase>install</phase>
<goals>
<goal>install-file</goal>
</goals>
</execution>
Thank you so much for solving this. This was one of the most annoying things from day 1. But since no one else complained I thought it was only my local Maven configuration. So I just switched to "offline mode" as workaround ;)
I already fixed the POMs so you can already get the new version form GitHub. Thanks a lot for valuable feedback and hope you'll find liferay-maven-sdk useful.
Hi there,
I tried the steps aove for my own portlet, but when I perform mvn package after I create the service.xml with my own stuff theres nothing create in my project, no java-service-api or anything else. What goes wrong?
greetz
When you call
set the profile to "build-service". Inside pom.xml a default profile is set which is active by default. Set the profile "build-service" to default profile or call After calling, there should be somenew classes inside your project.Correct. This was changed in 5.2.3-r2 so the ServiceBuilder does not get executed on every build. If there are no changes, it does nothing but takes some additional seconds. In general you need to execute
if you create/change "service.xml" or modify service implementation classes. In all other cases you don't need to invoke ServiceBuilder so you can simply do
Hi Milen
I did made conversion of your liferay-maven-sdk to Liferay 5.2 EE sp2, just by replacing from pom.mx 5.2.3 to 5.2-ee-sp2
update the libraries and liferay xml files.
It seems to work well. You made good design approach by making build and everyone can install those archetypes to their local repository. At LR-EE this is evenmore important.
Thanks for making my life easier.
- Sampsa
Hey, I'm using the maven-SDK coming from liferay 6.0 trunk, and I have the same issue as Christian does. The profile is activated, but it doesn't generate the classes all so that there is nothing to compile...no sources at all. I was trying to figure out what's going on from maven debug output but didn't help.
Christian was referring to liferay-maven-sdk I wrote for 5.2.3 version, and the problem was that "build-service" profile is not activated automatically since 5.2.3-r2 .
I beleive the maven-SDK you are referring is the one described here: http://www.liferay.com/web/mika.koivisto/blog/-/blogs/liferay-maven-sdk
What I can suggest is to:
- double check if you have all artifacts installed and configured
- make sure correct profile is activated
- run maven with -X (debug) and check dependencies, classpath, ...
- ask for help on Liferay forums
So there is an official maven-sdk for Liferay 6 and your un-official maven-sdk for Liferay 5.2.
Is there any links between the 2 ?
- Are they compatible ? (We will be able to upgrade without trouble ?)
- It is some kind of back-port or are they each starting from scratch ?
How could I decide if it is better to go for the official beta or the un-official release ??
Thanks
Hi Bruno
When I released liferay-maven-sdk for 5.2 (November 2009), the official maven SDK from Liferay din't yet exist. I knew Liferay is working on it but I didn't have access to any source codes or even design approaches taken into account. Therefore liferay-maven-sdk was written from scratch and it's not based or related to any work from Liferay team.
I didn't have a chance yet to play with the official maven SDK but according to what Mika Koivisto says (http://twitter.com/koivimik/status/6319329361) it is also written from scratch and does not make use of my code.
So which one to use? As far as I know the official maven SDK is not backported to support Liferay 5.2. On the other hand I do not intend to compete with Liferay and (unless there is community demand) liferay-maven-sdk will not evolve to support Liferay 6.0. So the question is which Liferay version do you use?
As far as upgrade/migration is concern there shouldn't be any big issues in my opinion. Both SDK are only build systems, which do not have big impact on your source code. If you do not use ServiceBuilder it should be only a matter of reconfiguring your POM. With ServiceBuilder it may get a bit more complicated but on the other hand there are probably some changes in Liferay's 6.0 ServiceBuilder itself which will force you to do some kind of refactoring anyway.
Hello Milen,
Thanks for a great sdk, great job! :)
doesn't seem to do the trick, even though I do get some of the liferay-maven-sdk jars deployed. I'm still missing all the libraries (i.e. jars in portal/libraries and sdk/libraries folders). Am I missing something here? Or should I use with different kind of configuration to be able to install stuff to my internal repo (instead of the local repository)?However, I was wondering if it's possible to install sdk to internal repository, instead of the local repository? I have Sontaype Nexus repository configured and I'd like to put liferay-maven-sdk there the same way it's installed to my local repository.
Thanks a million! ;)
Hi Peders,
I haven't actually tested it but I believe you need to modify the POMs to tell maven to deploy the library files. Have a look at the end of POMs in "liferay-maven-sdk/libraries/liferay-*-lib". You will notice how the "maven-install-plugin" is configured to install the libraries during "install" phase. I believe you can configure "maven-deploy-plugin" in a similar way to get the libraries deployed to your Nexus repository. Have a look at http://maven.apache.org/plugins/maven-deploy-plugin/deploy-file-mojo.html for list of parameters you can use with "deploy-file" goal.
Milen,
I tried what you suggested and voilĂ , it worked! ;) I simply copy-pasted the declaration of maven-install-plugin for each library pom files, changed install phase to deploy phase and ran mvn deploy. Now all the dependencies (including library jars) are sitting nicely in my nexus repo and my life just got a little easier :)