Sun GlassFish Web Space Server 10.0 Developer's Guide

Developing Services Using the Service Builder Plug-in in NetBeans Portal Pack 3.0

The Service Builder plug-in is part of the NetBeans Portal Pack 3.0 release. The Service Builder plug-in in the Sun GlassFish Web Space Server provides a GUI editor to automate the creation of interfaces and classes that are used by a given portal or a portlet.

This section explains how you can use the Service Builder plug-in to automatically create the interfaces and classes that can be used by a portlet. To do this, you need to create a portlet and deploy it on Sun GlassFish Web Space Server.

Creating a Portlet Using NetBeans Portal Pack 3.0

Create a portlet called MovieDatabase using the NetBeans IDE.

ProcedureTo Create a Portlet Using the NetBeans IDE

  1. On NetBeans IDE, click File and select New Project.

    The New Project window appears.

  2. From the list of Projects in the Choose Project pane, select Web Application and click Next.

  3. In the New Web Application window, specify the name of the project as MovieApplication.

  4. Click Next.

  5. In the Server and Settings pane, select Sun GlassFish Web Space Server from the Server drop down menu.

  6. Click Next.

  7. From the Frameworks list box, select Portlet Support framework.

  8. In the Portlet Support Configuration pane, select Create Portlet and change the Portlet Class Name to MovieDataEntryPortlet.

  9. Click Finish.

    The NetBeans IDE creates a portlet web application and generates all the required files for the web application.

Creating the service.xml File

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

ProcedureTo Create a service.xml File

  1. From the MovieApplication portlet, right click Web Pages and select New.

  2. Click Other.

    The New File wizard appears.

  3. In the Choose File Type pane, choose MovieApp from the Project drop down menu.

  4. Select Sun GlassFish Web Space Server Portlets from Categories and Service Builder XML from File Types.

  5. Click Next.

    The Name and Location window appears.

  6. Specify the File Name as service and click Finish.

    The service.xml file is created inside the web directory. The Design tab is created where you can see the GUI editor for the service.xml.

ProcedureCreating Entities in the service.xml GUI Editor

  1. Click Add.

    The Service Definition window appears.

  2. Specify the Entity Name as Movie and the Table Name for the entity as tb_movie.

  3. Select the Remote Service checkbox and click Add.

    The entity Movie is created with the Remote value as true.

  4. Change the default Package Path name to com.sample.movie.

    After creating an entity, you need to add columns for the entity.

  5. Click Add under the Columns tab.

    The Column Details window appears.

  6. Specify the Name of the column as name and the column type as String.

    You can choose the column type as String, Double, and Date from the drop down list.

  7. Select the Primary Key checkbox and click Add.

    A column is created with the Column Name as name and type as String.

  8. Add three more columns with the Column Types cast, story, and releaseDate with types String, String, and Date respectively.

    You can see that there are four columns created for the Movie entity.

  9. Add another entity called MovieFeedback with the Table Name as tb_moviefeedback.

  10. Add columns such as id, movieName, rating, and feedback with types long, string, string, and string respectively.

    You can see that there are four columns created for the MovieFeedback entity.

    You need to add a Finder method for the MovieFeedback entity.

  11. Click the Finders tab.

  12. Click Add Finder.

    The Finder Details window appears.

  13. Specify the Finder Name as MovieName.

  14. From the Available Columns pane, select movieName and click Add.

    The movieName method is added under the Finder Columns pane. Since the movieName is the finder method, you can search the details of a movie by the name of a movie.

    If you want to set the properties for the finder method, click Properties and choose any Comparators.

  15. Click Add.

    You can see that the finder method is added to the service.xml and the same method appears in GUI editor.

ProcedureGenerating Services

  1. From the service.xml GUI editor, click the Generate Services tab.

    You can see the creation of classes, services, and data models that are required for database interaction.

  2. In the GUI editor, click the Local Methods tab and click Go To Source.

    The local service file for the Movie entity appears.

  3. In the local service class file, add a method called getMovies to return the movies listed in the Movie entity.

    You can use the moviePersistance.findAll method to find the movies present in the Movie entity.

  4. Save the class file.

    In the GUI editor, you can see the getMovies method under the Local Methods tab.

  5. Click Generate Services.

    You can see that all the services are generated again.

ProcedureCreating the View Page for the Movie Data Entry Portlet

  1. From the Projects pane, click the MovieDataEntryPortlet_view.jsp file.

  2. Add code to the MovieDataEntryPortlet_view.jsp file to create a GUI form to specify the movie name, cast, and release date.

    This information is stored in the Movie entity.

  3. From the Projects pane, click the MovieDataEntryPortlet.java file under the com.test folder.

    The MovieDataEntryPortlet.java file appears in the right pane. You can see all the parameters to display the movie details are listed in this file. You can also add other required parameters in this file.

    If you expand the folders in the Project pane, you can see the classes that are automatically generated by the service builder infrastructure. If you click the Files tab, you can see all the .jar and .sql files that are automatically created. When you deploy the portlet on Sun GlassFish Web Space Server 10.0 or Liferay portal server, the .sql files are called and the tables will be created automatically.

ProcedureCreating the MovieList Portlet

  1. Right click on the MovieApp portlet, select New and click Other.

    The New File window appears.

  2. Select Portlet from Categories and Portlet from File Types.

  3. Click Next.

    The New Portlet window appears.

  4. In the Name and Location pane, specify the Class Name as MovieList, Package as com.test, and Portlet Name as MovieList.

  5. Click Next.

  6. In the Create Jsps for Portlet pane, specify the View Jsp as MovieList_view.jsp.

  7. Click Finish.

    The MovieList portlet is created. This portlet lists the movies in the Movie portlet database or Movie entity.

  8. From the Projects pane, click the MovieList_view.jsp file.

    The MovieList_view.jsp file appears in the right pane.

  9. Add code to the MovieList_view.jsp file to create a GUI for the MovieList portlet.

    Using this JSP file, you can fetch all the data from Movie entity from the Movie services class and display the movie details in the GUI form. In this JSP file, you can use the getMovies method to fetch the movie data.

  10. From the Projects pane, right click on the MovieApp portlet and click Run to deploy the portlet.

    The Sun GlassFish Web Space Server server is started. The MovieDataEntry and MovieList portlets are deployed successfully.

  11. In the Sun GlassFish Web Space Server 10.0, login as Admin user.

  12. Add the MovieDataEntry and MovieList portlets in a portal page.

ProcedureAdding Portlets in Sun GlassFish Web Space Server

  1. From the Welcome menu, click Add Application.

    The Add Application window appears. You can see the MovieDataEntry and MovieList portlets under User_Portlets.

  2. Click Add to add the MovieDataEntry and MovieList portlets in the portal page.

  3. In the MovieDataEntry portlet, add new records by specifying movie name, cast, story, and release date.

    You can see that the movie records that you have entered in the MovieDataEntry portlet appears in the MovieList portlet.

Defining the Database Structure

You need to define the structure of a database to expose your services to a local or remote service. You can define the structure of the database using the Service Builder plug-in user interface. After you define the structure, you can generate the service.xml file using the Generate service button.

For example, if you want to create a movie database, define the required elements in the Service Builder plug-in. Using the defined elements, the Service Builder plug-in automatically creates the service.xml file with the defined values.

Structure of service.xml File

The service.xml file is available in the /docroot/WEB-INF folder. A typical service.xml file looks like this.


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

<service-builder package-path="com.sample.portlet.library">
		<namespace>Library</namespace>
		<entity name="Book" local-service="true" remote-service="false">

			<!-- PK fields -->

			<column name="bookId" type="long" primary="true" />

			<!-- Group Instance -->

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

			<!-- Audit fields -->

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

			<!.. Other Fields ..>

			<column name="title" type="String" />
		</entity>
</service-builder>

Let us look at the elements and their functions in the service.xml file.


<service-builder package-path="com.sample.portlet.library">

The package-path element specifies the package path that the class will generate to. In this example, classes will generate to WEB-INF/src/com/sample/portlet/library/.


<namespace>Library</namespace>

The namespace element must be a unique namespace for this component. The names of tables names will be prepended with this namespace element.


<entity name="Book" local-service="true" remote-service="false">

The entity name is the database table you want to create.

Generating a Service Layer

Open up a command prompt, navigate to your portlet's root folder and type the following command.

ant build-service

The service layer has been generated successfully when you see BUILD SUCCESSFUL message.

Creating the Service Layer Class

Open the BookLocalServiceImpl.java file, which is available in /docroot/WEB-INF/src/com/sample/portlet/library/service/impl/. We will be adding the database interaction methods to this service layer class. Add the following method to the BookLocalServiceImpl class.


public Book addBook(long userId, String title)
			throws PortalException, SystemException {
		User user = UserUtil.findByPrimaryKey(userId);
		Date now = new Date();
		long bookId = CounterLocalServiceUtil.increment(Book.class.getName());

		Book book = bookPersistence.create(bookId);

		book.setTitle(title);
		book.setCompanyId(user.getCompanyId());
		book.setUserId(user.getUserId());
		book.setUserName(user.getFullName());
		book.setCreateDate(now);
		book.setModifiedDate(now);
		book.setTitle(title);
		return bookPersistence.update(book);
}

Before you can use the code, you must regenerate the service layer class. Navigate to the root folder of your portlet, and type the ant build-service command. You can now add a new book to the database by making the following call.


BookLocalServiceUtil.addBook(userId, ?A new title?);

Security and Permissions Service

The Sun GlassFish Web Space Server implements a fine-grained permissions system, which developers can use to implement access security into their custom portlets, giving administrators and users a lot more control over their portlets and content. This section of the document will provide a reference for implementing security into custom portlets.

Overview

    Adding fine-grained permissions to custom portlets consists of four main steps. This procedure is also known as DRAC.

  1. Define all resources and their permissions.

  2. Register all the resources mentioned in step 1 into the permissions system. This is also known simply as “adding resources”.

  3. Associate the necessary permissions to these resources.

  4. Check permission before returning resources.

Implementing Permissions

In this section, each of the four main steps in adding the security feature available in the Sun GlassFish Web Space Server into custom portlets (built on top of the Sun GlassFish Web Space Server portal) will be explained. The following are two definitions that are important to remember.


public BlogsEntry addEntry(
			long plid, String title, String content, int displayDateMonth,
			int displayDateDay, int displayDateYear, int displayDateHour,
			int displayDateMinute, String[] tagsEntries,
			boolean addCommunityPermissions, boolean addGuestPermissions,
			ThemeDisplay themeDisplay)
		throws PortalException, SystemException {

		PortletPermissionUtil.check(
			getPermissionChecker(), plid, PortletKeys.BLOGS,
			ActionKeys.ADD_ENTRY);

		return blogsEntryLocalService.addEntry(
			getUserId(), plid, title, content, displayDateMonth, displayDateDay,
			displayDateYear, displayDateHour, displayDateMinute, tagsEntries,
			addCommunityPermissions, addGuestPermissions, themeDisplay);
}

Before the addEntry(...) method calls BlogsEntryLocalServiceUtil.addEntry(...) to add a blogs entry, it calls PortletPermission.check(...) to validate user permission. If the check fails, a PrincipalException is thrown and an entry will not be added. Note the parameters passed into the method. Again, the getPermissionChecker() method is readily available in all ServiceImpl classes. The plid variable is passed into the method by its caller (most likely from a PortletAction class). PortletKeys.BLOGS is just a static String indicating that the permission check is against the blogs portlet. ActionKeys.ADD_ENTRY is also a static String to indicate the action requiring the permission check. You are encouraged to do likewise with your custom portlet names and custom action keys.

Whether you need to pass in a portlet layout ID (plid) depends on whether your custom portlet supports multiple instances. Let us take a look at the message board portlet for example. A community may need three separate page layouts, each having a separate instance of the message board portlet. Only by using the portlet layout ID will the permission system be able to distinguish the three separate instances of the message board portlet. This way, permission can be assigned separately in all three instances. Though in general, most portlets will not need to use the portlet layout ID in relation to the permission system.

Since the ServiceImpl class extends the PrincipalBean class, it has access to information of the current user making the service request. Therefore, the ServiceImpl class is the ideal place in your business layer to check user permission. Liferay's convention is to implement the actual business logic inside the LocalServiceImpl methods, and then the ServiceImpl calls these methods via the LocalServiceUtil class after the permission check completes successfully. Your PortletAction classes should make calls to ServiceUtil (wrapper to ServiceImpl) guaranteeing that permission is first checked before the request is fulfilled.

Checking model resource permission is very similar to checking portlet resource permission. The only major difference is that instead of calling methods found in the PortletPermission class mentioned previously, you need to create your own helper class to assist you in checking permission. The next section will detail how this is done.

It is advisable to have a helper class to help check permission on your custom models. This custom permission class is similar to the PortletPermission class but is tailored to work with your custom models. While you can implement this class however you like, we encourage you to model your implementation after the PortletPermission class, which contains four static methods. Let us take a look at the BlogsEntryPermission class.


public class BlogsEntryPermission {

		public static void check(
				PermissionChecker permissionChecker, long entryId, String actionId)
			throws PortalException, SystemException {

			if (!contains(permissionChecker, entryId, actionId)) {
				throw new PrincipalException();
			}
		}

		public static void check(
				PermissionChecker permissionChecker, BlogsEntry entry,
				String actionId)
			throws PortalException, SystemException {

			if (!contains(permissionChecker, entry, actionId)) {
				throw new PrincipalException();
			}
		}

		public static boolean contains(
				PermissionChecker permissionChecker, long entryId, String actionId)
			throws PortalException, SystemException {

			BlogsEntry entry = BlogsEntryLocalServiceUtil.getEntry(entryId);

			return contains(permissionChecker, entry, actionId);
		}

		public static boolean contains(
				PermissionChecker permissionChecker, BlogsEntry entry,
				String actionId)
			throws PortalException, SystemException {

			return permissionChecker.hasPermission(
				entry.getGroupId(), BlogsEntry.class.getName(), entry.getEntryId(),
				actionId);
		}
}

    Again, the two check(...) methods are meant to be called in your business layer, while the two contains(...) methods can be used in your JSP files. As you can see, it is very similar to the PortletPermission class. The two notable differences are:

  1. Instead of having the portletId as one of the parameters, the methods in this custom class take in either an entryId or a BlogsEntry object.

  2. None of the methods need to receive the portlet layout ID (plid) as a parameter. (Your custom portlet may choose to use the portlet layout ID if need be.)

Let us see how this class is used in the blogs portlet code.


public BlogsEntry getEntry(String entryId) throws PortalException, SystemException {
		BlogsEntryPermission.check(
			getPermissionChecker(), entryId, ActionKeys.VIEW);
		return BlogsEntryLocalServiceUtil.getEntry(entryId);
}

In the BlogsEntryServiceImpl class is a method called getEntry(...). Before this method returns the blogs entry object, it calls the custom permission helper class to check permission. If this call does not throw an exception, the entry is retrieved and returned to its caller.


<c:if test="<%= BlogsEntryPermission.contains(permissionChecker, entry, ActionKeys.UPDATE)
%>">
		<portlet:renderURL windowState="<%= WindowState.MAXIMIZED.toString() %>"
var="entryURL">
			<portlet:param name="struts_action" value="/blogs/edit_entry" />
			<portlet:param name="redirect" value="<%= currentURL %>" />
			<portlet:param name="entryId" value="<%= entry.getEntryId() %>" />
		</portlet:renderURL>

		<liferay-ui:icon image="edit" url="<%= entryURL %>" />
</c:if>

In the view_entry_content.jsp file, the BlogsEntryPermission.contains(...) method is called to check whether or not to show the edit button. That is all there is to it!

Let us review what we have just covered. Implementing permission into your custom portlet consists of four main steps. First step is to define any custom resources and actions. Next step is to implement code to register (or add) any newly created resources such as a BlogsEntry object. The third step is to provide an interface for the user to configure permission. Lastly, implement code to check permission before returning resources or showing custom features. Two major resources are portlets and Java objects. There is not a lot that needs to be done for the portlet resource to implement the permission system since Liferay Portal has a lot of that work done for you. You mainly focus your efforts on any custom Java objects you have built. You are now well on your way to implement security to your custom Liferay portlets!