Portal Pack : Write Database Portlet using Service Builder Plug-in

The service builder framework in Liferay represents the database layer and all the interactions with database are done through service builder infrastructure. So in this blog, I will explain how you can use service builder framework inside your custom portlet using NetBeans 6.5 & Portal Pack 3.0. To use service builder framework, you first need to create a service xml and then generate the required code. The Portal Pack here helps you by providing a nice GUI editor for service.xml file where you can define the entities or database structures and from the same GUI you can generate the services code which can be used inside your portlet.

I have taken an example of Simple Form Submission Portlet here. Using this portlet you can submit customer details which will be stored inside liferay's database and the same portlet will also show the no of customer records present in the database.

First create a Webapplication with "Portlet Support" framework and add a new Portlet to it. Now you need to create the service.xml file. Let's create the portlet application as "CustomerApp" and a portlet "CustomerPortlet" inside it.

Creating the service.xml File

After creating a portlet application, you need to create a service.xml file inside the portlet application.

To Create a service.xml File

  1. From the CustomerApp application, right click on Web Pages and select New > Others > Web Space/Liferay Plugins > Service Builder XML

  2. Enter the file name as service. Make sure the "Folder" is selected as web. Then click Finish.

  3. The service.xml file is created inside the web directory and the IDE opens the service.xml inside the editor area.

You can see two tabs "Design" and "XML" in the editor. The "Design" tab helps the user to add/modify entities through GUI and using "XML", user can directly modify the xml.

Fig: 1


Now we are ready to add entities to our service xml.

To Create Entity

  1. Click on "Add" button.

    The Service Definition window appears. 

  2. Specify the Entity Name as "CustomerDtls" and table name as "CustomerDtls". The Entity name and table name doesn't need to be same.

  3. Now Click "Add". So a new entity called "CustomerDtls" is now created.
  4. Change the default Package Path name to com.sample.customer and namespace to "customer". The namespace should be unique for every portlet application. No two portlet applications should have the same namespace.

Add Columns

After creating an entity, you need to add columns for that entity. So to create columns for an entity

  • Select an entity. 
  • Click "Add" under the Columns tab as shown in the Fig : 3.
 

Fig : 3
  • The Column Details window appears. 
  • Specify the Name of the column as "id" and the column type as long. You can choose the column type as String, Double, and Date from the drop down list. 
  • Select the Primary Key checkbox and click Add. So for this entity, id is the primary key. A column is created with the Column Name as id and type as long
  • Add two more columns with column name name, age with types String, int respectively. 
  • You can see that there are three columns created for the "CustomerDtls" entity. (Fig: 4)
Note: There should be atleast one primary key defined for an entity.
 

                                                    Fig : 4

Add Finder Methods

You can also add your custom finder methods to the entity from "Finders" tab. In "Finders" tab, click "Add Finder" to add finder methods. It provides a GUI where you can select different columns required for your finder methods.(Fig. 5)



Fig. 5

Generating Services

  • From the service.xml GUI editor, click the "Generate Services" button. 
  • You can see the creation of classes, services, and data models that are required for the database interaction. The service api classes are generated under $project/services directory and added to the project classpath. You should not do any modification in those classes as those will be overwritten next time when you run "Generate Service". But the implementation classes which can be modified by the user, are added to the project source path and you can modify them as you want. But after modifying anything in the service implementation classes, you need to run "Generate Service" again.
  • By default the generated service api jar will be bundled inside the portlet war file. But if you want other applications to access your services then the service api jar file needs to be there in the server classpath. You can do that by changing the preference which can be accessed by clicking on "Preferences" button. (Fig: 6)

Fig: 6
  • Now in the GUI editor, click the Local Methods tab. This tab lists local method defined in the local service implementation. Now you can select "Go to Source"  to go the local service implementation. In our example this is "CustomerDtlsLocalServiceImpl.java" file. But we don't need to add any local method for our current example.

Creating the View Page for Customer Portlet

  • From the Projects pane, click the CustomerPortlet_view.jsp file. 
  • Add code to the CustomerPortlet_view.jsp file to create a HTML form to specify the id,name and age of a customer. This jsp also shows the no of customers in the database. Replace the CustomerPortlet_view.jsp code with following code snippets.
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>

<%@ page import="javax.portlet.*"%>
<%@ page import="com.example.customer.service.*"%>
<%@ page import="com.example.customer.model.*"%>
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>

<portlet:defineObjects />
<%PortletPreferences prefs = renderRequest.getPreferences();%>
<table>
<form method="POST" action="<portlet:actionURL/>">

    <tr>
        <td>Customer ID:</td>
        <td><Input type="text" name="id"/></td>
    </tr>
    <tr>
        <td>Name:</td>
        <td><Input type="text" name="name"/></td>
    </tr>
    <tr>
        <td>Age:</td>
        <td><Input type="text" name="age"/></td>
    </tr>
    <tr>
        <td><Input type="submit"/></td>
    </tr>
</form>
</table>
<H3> No of Customers in Database :
    <%
        out.println(CustomerDtlsLocalServiceUtil.getCustomerDtlsesCount());
    %>
</H3>

 

  • From the Projects pane, click the CustomerPortlet.java file under the com.test package. Add the following code to processAction method. This method gets the data from the browser and add customer details to the database.
public void processAction(ActionRequest request, ActionResponse response) throws PortletException,IOException {
     
        try {
            long id = Long.parseLong(request.getParameter("id"));
            String name = request.getParameter("name");
            int age = Integer.parseInt(request.getParameter("age"));
            CustomerDtls customer = CustomerDtlsLocalServiceUtil.createCustomerDtls(id);
            customer.setId(id);
            customer.setName(name);
            customer.setAge(age);
            CustomerDtlsLocalServiceUtil.addCustomerDtls(customer);
            System.out.println("Added");
        } catch (Exception ex) {
            ex.printStackTrace();
           
        }
    }

 

Deploy And Test

So we are now ready to deploy the portlet application. Right click on the portlet project and select "Deploy" to deploy the portlet on Liferay Portal Server. I am assuming you have selected "Liferay Portal Server"  as runtime for the portlet application. The required table is created automatically during the deployment time, so ideally you don't need to create the table manually. But incase you get "Table not Found Exception" while accessing the portlet, you can run the generated table.sql manually to create the required tables. The table.sql can be found under "WEB-INF/sql" directory.

Now access your liferay portal from the browser and add the new "CustomerPortlet" to a page. You can see a simple form for customer data and below that no of customers in database which is initially "0". Now try to add a customer and after successful addition of data to the database, the "No of Customers in Database" will be changed to 1.
 
Fig: 7
Blogs
This seemed to be a nice example, so I had to try it, but am I suprised ... it did not work emoticon I might be stupid maybe, but now I am really tired of that nothing wont work for me in Liferay development.
Now when I deploy this I get the answer that package com.liferay.util.services does not exist. Where do I get this and how do I get it into Netbeans? I have Liferay 5.2.1, Tomcat, Liferay SDK and Netbeans 6.5.
It is not the only error I get either but the first.
In CustomerPortlet_view.jsp there is an error on the first line. It says for "<%@page contentType="text/html"%>" that package com.example.customer.service and com.example.customer.model does not exist. How do I solve that?
In CustomerPortlet.java there is errors in the processAction method. It says "Cannot find symbol" in CustomerDtls, and the same for setId, setName, setAge and so on.
It would be most greatful if you have the time to give me a hint for what I am doing wrong. I have followed your instructions line by line for two times, so I dont think I have done anything wrong, but probably there is something missin in my installation.

/Jane
>>In CustomerPortlet_view.jsp there is an error on the first line. It says for "<%@page >>contentType="text/html"%>" that package com.example.customer.service and >>com.example.customer.model does not exist. How do I solve that?
I hope you have done "Generate Service" . To make sure the services are generated properly, check in your file system if there's a directory called "services" under your project directory and also check the contents of that directory.
It also generate some implementation classes under your source root, just check if those packages exist or not.

Ideally all the generated services classes are automatically added to the project's classpath. You can check the compile library of the project.

Let me know if that helps. If that doesn't work you can send me your project if possible.
Yes I have done "Generate Service" and I have a directory called serivces with a lot of files in.
I would be glad if you could take a look at my files, but where can I send them? Can I send them to you if I become your friend from your profile? Otherwise you can send an answer to my mailadress, jane.eriksson@miun.se, and I will post the files back to you. Is that ok?

/Jane
Did not manage to generate service. I got this message:

classpath could not be modified
please add $project_dir/service/glasses folder to your project classpath
but don't pakage this folder in your webapp war
Hi,
I follow the steps, when i generate service. It shows an error " please select the target runtime as sun glassfish web space /liferay portal". I am using liferay 5.2.3 tomcat bundled version ,netbeans 6.5,portal pack 3.0.1 version. kindly help me fix this problem



Thanks
Arun
Yap, I got the exact same problem. Any ideas?
Are you facing the same "Cannot find Symbol" in java file ? If yes, did you try fixing import by fix import option. (Right click on editor > Fix Imports).

Have you selected the project's target runtime as Liferay Server ?
Hi Satya,

I just did what you suggested. The Fix Imports option doesn't do anything.
And the project's runtime is a Liferay Server. I can deploy portlets to my Liferay instance.

Cheers
If possible send me your sample project to me. I will check that. Also you can send me the screenshot of exact error.
my mail id is ranjansatya at gmail dot com
The "Fix import" fixed one kind of error for me, but now get an other error about a missing package called com.liferay.util.service. Is there anyone who gets that error to?
I have Netbeans 6.5.1 and Liferay Plugins SDK 5.2.2. Can it depend on Netbeans or what?
Jane,

Make sure you have selected the target runtime as "Liferay Portal Server" for the project. When you set the server as LR, then only portal-service.jar (required for com.liferay.util.service package) is added to the project's compile classpath.

Thanks !!!
Hi,
do you mean that I must have "Liferay Portal Server" chosen as server for the project? If that is what you mean I already have that and have had so all the time for this project.
The file portal-service.jar does not exist in my LiferaySDK environment at all. Is that wrong? I made a search in the Liferay portal environment and then I found it under \liferay-portal-5.2.1\tomcat-5.5.27\common\lib\ext.
Me again ...
I just want to add that in my project CustomerApp opened in NetBeans I can see the file portal-service.jar under Libraries/Liferay Portal Server 5.1.x/5.2.x, so that must be correct, or ...
Jane,

Sorry for the confusion, the com.liferay.util.service package is there inside util-java.jar file. I tried with LR 5.2.2 (with Tomcat 5.5 bundle) and I could see the util-java.jar under the Libraries/Liferay Portal Server. You can expand util-java.jar to see the com.liferay.util.service package. But in someway if your IDE still doesn't recognize that package, I would suggest you to remove the liferay server from IDE and then reconfigure it again.

Let me know if that solves your issue.
Hi,
That did it as a matter of fact ... emoticon
I didn´t have any util-java.jar at all under Libraries/Liferay Portal Server, but when I did as you kindly told me, removed the Liferay server and added it again, and after a restart of Netbeans it was there. Strange, is´nt it? I was then able to Run the project and could finally test the new portlet.
I have some questions thow:
1. Where does the information go that I add in the portlet? I cant find it.
2. When I run the project two pages of Liferay was opened. Why?
3. I still get some errors about zip-files that cant be opened and servlet default threw exception java.lang.NullPointerException and some warnings to.
Is it possible to also send you the error-file and maybe get some tip again about what it can be. As I said I can run the project now, but it doesnt feel good with the errors.

Thanks again!
Jane,

Good to hear that it worked. Strange, how the util-java.jar got removed automatically first time.
>>1. Where does the information go that I add in the portlet? I cant find it.
Do you mean output ? If yes, then you need to add that portlet on your portal page using "Add Application".

>>2. When I run the project two pages of Liferay was opened. Why?
Not sure. Sometime when the liferay is started in the developer mode it starts the browser automatically. Also netbeans launches browser after server restart from IDE. May be both are happening in your case. To avoid this, you can do only "Deploy" instead of "Run". And later go to the browser to see the output.

>>3. I still get some errors about zip-files that cant be opened and servlet default threw exception java.lang.NullPointerException

Send me the error log to my mail id.
Thanks !!!
Satya,
>>1. Where does the information go that I add in the portlet? I cant find it.
Maybe a stupid question ... I have added the portlet to my portal and have added a couple av names and can se that it works. The number of customers in the Database increases as it should, but I want to look in the Database and se the posts from there. Where is the Database so I can watch them?
I will send the mail with my errors then.
Thanks again!
Jane,
You could see the data using NetBeans's database explorer or if you are using MySQL DB then you could query mysql. But if you are using HSQL DB (default DB in Liferay) you need a HSQL DB explorer or you can configure your NetBeans's DB explorer to show your HSQL DB.
I was able to take the war file created and copy it to the deploy folder. I'm quite dependent on Tomcat as I have a number of other webapps.
Well, the util-java.jar wasn't under Libraries/Liferay Portal Server, but I found in another location in the Liferay folder: /util-java. I imported it to the project and everything seems to work.

Thanks a lot emoticon
This is really great, it was working fine, I added the Columns then clicked on "Generate Services". It generated all the classes and I could see the newly generated source code in the folders although the Columns went blank and when I clicked "Add" in "Columns" section again nothing happened. The Design does not match the XML. I've attempted to make a new project then deleted them all and reinstalled several times and I still get the same result.
I running this on a mac, any ideas?
I have submitted a bug to NetBeans.org. I also tried this on a windows machine (vista) and it wouldn't let me click "Add" even the first time I tried it. PLEASE HELP.
http://www.netbeans.org/issues/show_bug.cgi?id=162004
Brant,

Thanks for filing the issue. First you have to select any entity in "Entity" section and then you have to click "Add Column" in column section to add new columns. Sometime after adding columns, the focus goes away automatically from the selected entity and you see blank column section (it's a bug). You have to select a particular entity first to see the columns defined for that entity and then click add to add column. Hope that helps !!
Let me know if it works for you..
Okay, I had the same issue as above where I had to add in the java-util but after doing that I still get an issue with the added code:
/Users/Brant/NetBeansProjects/CustomerApp/src/java/com/test/CustomerPortlet.java:21: cannot find symbol
symbol : class CustomerDtls
location: class com.test.CustomerPortlet
CustomerDtls customer = CustomerDtlsLocalServiceUtil.createCustomerDtls(id);

I have CustomerDtlsServiceImpl but no CustomerDtlsLocalServiceUtil, was this supposed to be created? I am using the PortalPack 3.0.

Brant
Okay, this is hopefully the last issue. When I click deploy I get this in the console:
init:
deps-module-jar:
deps-ear-jar:
deps-jar:
library-inclusion-in-archive:
library-inclusion-in-manifest:
compile:
compile-jsps:
/Users/Brant/NetBeansProjects/CustomerApp/nbproject/build-impl.xml:552: Problem: failed to create task or type nbdeploy
Cause: The name is undefined.
Action: Check the spelling.
Action: Check that any custom tasks/types have been declared.
Action: Check that any <presetdef>/<macrodef> declarations have taken place.
BUILD FAILED (total time: 0 seconds)
Brant,

Regarding your deploy issue, I am not sure why it's happening as it's not reproducible always. I could see some forum posts on that issue but got no answer
http://forums.netbeans.org/post-29506.html
http://forums.netbeans.org/ptopic4031.html

I will try to investigate what exactly is going wrong in your case ? (Looks like this issue is only on MAC sometimes)

Meanwhile could you please try to see if you can deploy a normal web application on Glassfish server from your NetBeans IDE ?

Thanks,
I am not able to see the JSON service being generated. Is this supposed to be accessible during generation. How do I get access to the JSON service for using remote clients.

Regards,
Brant
Great tutorial on using the portalpack service utility.

Can you extend it and show us how to Update, Delete and View the data?

Thanks!
Do you know how to show the list in a search container?

Like this one? "http://www.liferay.com/web/guest/community/wiki/-/wiki/Main/SearchContainer"
This wiki explain everything about Search Container.
Here is a sample code snppet where I am showing the list of movies from databse. Hope it will be useful for you.

<%
List headers = new ArrayList();
headers.add("Movies");
headers.add("Release Date");
SearchContainer sc = new SearchContainer();
sc.setHeaderNames(headers);
sc.setEmptyResultsMessage("No movies found");

List resultRows = sc.getResultRows();

Collection<Movie> movies = MovieLocalServiceUtil.getMovies();
Iterator it = movies.iterator();
int i =0;
while (it.hasNext()) {
Movie m = (Movie) it.next();
ResultRow row = new ResultRow(m.getName(), m.getName(), i++);
PortletURL url = renderResponse.createActionURL();
url.setParameter("movie_name", m.getName());
row.addText(m.getName(), url);
Date d = m.getReleaseDate();
String ds = "";
if(d != null)
ds = d.toString();
row.addText(ds);
resultRows.add(row);
}
%>

<ui:search-iterator searchContainer="<%=sc%>"/>
Hi,

I've been following your tutorial and everything works great. But instead of letting the user specify the customer ID, I want to use the Counter Service.
I'm able to deploy this version with no erros, but I always get execution errors.
Have you ever used Counter Service?
Hello again,

Regarding my last post, the issue is fixed.
Now, regarding your last post on SearchContainer, I have some doubts emoticon

Is the code you posted enough to display the table contents or is there something missing?

Cheers mate
>>Great tutorial on using the portalpack service utility.
>>Can you extend it and show us how to Update, Delete and View the data?

I will try to write a new blog which will cover these.
This I am looking forward to. Since I am not a programmer, this entire system is perfect to allow me to use Liferay as an important part of the MMO I work for.

I have now evaluated around 20 systems, and I am hoping that the easy portlet development you are championing will mean that Liferay is our final choice.
Currently the JSON services are not being generated for services in a stand alone portlet. But if you find any way to make the json service work in external portlet, please let me know.
This example didnt work for me emoticon for portalpack3.01) Allthough it worked for beta version.
Don know what is the problem. Compiles successfully but doesnt get deployed on liferay 5.2.2 through netbeans.
But if i copy the war file direcly to the tomcat depoy folder it works!
Informative.
Is there a similar set of instructions with eclipse?
This is a great tutorial, it shows what I exactly needed to work with liferay database in my portlets with changing the ext environment.

Now I have 2 issues:
- In the jsp file(view mode) the: out.println(CustomerDtlsLocalServiceUtil.getCustomerDtlsesCount()); is not showing the actual record count number. But i've tried to debug this inside the java code by simply
System.out.println(CustomerDtlsLocalServiceUtil.getCustomerDtlsesCount());
and the value is correct there. Why isn't this value updated in the jsp ???

Other problem is about the database connection. I would like to have my portlets working with a different database different from the one that liferay uses.
How can I specify that???

Thank you.
Hi,

Do you have any sugestion on how to add a table column as a Foreign Key (Which is a Primary Key from other table) ?

Thanks.
using liferay 5.2.3 tomcat 6 bundle with netbeans 6.7.1, portal pack 3.0.1.

Got the service generation to work once, and managed to get this portlet deployed.

Tried to do it again and it failed with: Exception in thread "main" java.lang.NoClassDefFoundError: com/liferay/portal/tools/servicebuilder/ServiceBuilder

Any ideas why this has happened, and which jar ServiceBuilder is in.
Just check if the target runtime for your application is still "Liferay Portal Server". The ServiceBuilder class is there inside portal-impl.jar which automatically gets added to the netbeans project classpath whenever Liferay Portal Server is selected as target runtime.
Thanks for the reply.

From the look I got the portal auto deploy dir wrong so it was looking in the wrong place.
Hi Satya,

I have follow all your instruction but it is not working at all.

In the jsp, there is no method "getCustomerDtlsesCount()". It is not generated in CustomerDtlsLocalServiceUtil. Same with the CustomerPortlet. No methods "addCustomerDtls(customer)" and "createCustomerDtls(id)" in CustomerDtlsLocalServiceUtil class.

However, I found the create() method in CustomerDtlsPersistenceImpl.class and
the addCustomerDtls() and getCustomerDtlsesCount() in CustomerDtlsLocalService.class.

I believe this CustomerDtlsLocalServiceUtil is autogenerated and can't be edited like the Impl classes. Can you tell me whats wrong with the code?

I'm using Liferay 5.1.1 and netbeans 6.5.

Thanks
hi faizul,
iam facing same problem. Methods in util class are not generated , only set and getService methods are present. iam also using netbeans 6.5, portal 5.1.1 and portal pack 3.0.1.

did u get the solution ?
With Netbeans 6.5.1 and 6.8 beta the add column button does nothing.
Is this an known issue.
I would like to try this example.
Does it work on any version Netbeans.?