官术网_书友最值得收藏!

Chapter 3. Saying Hello with a Portlet

JBoss portal server's primary strength lies in its ability to provide robust support for custom implementation of functionality using the portlet API. Portlets, the atomic unit of custom functionality, encompass the rich functionality and behavior we see aggregated on a portal web site.

In this chapter, we will try to understand the concepts and basics of building a functional portlet, by taking the traditional "Hello World" approach and building a portlet for it. The idea is to take a simple example and walk through the complete life cycle, from portlet development through to deployment, while introducing important concepts and configuration required for building a strong developmental foundation necessary for the complex use-cases introduced in the later chapters.

Portal page

Before we talk more about portlets, let us briefly refresh our perspective by looking at the broader picture of the relative positioning of portlets. In portals, a single aggregated set of information is displayed to the user using a portal page. A portal can have multiple portal pages. A portal page consists of various portlets with each contributing a page fragment or markup. Each portlet is a self-sufficient piece of functionality and is responsible for its own behavior and content. Each portlet resides in its own window and has its own controls. However, not all portlets necessarily have windows. There are some portlets that have pure content and no windows or controls.

When each portlet successfully executes and outputs a fragment, the portal page aggregates all of the content fragments from different portlets.

The following figure depicts all of the components of a portal page:

Portal page

JSR-168 and JSR-286 — Java portlet specification

Java Specification Request-168 is the foundational specification for building portlets. Similar to the servlet API, it is also the specification for portal server vendors to support portlets and applications built using the specification. The specification establishes standards for portlet interactions with portlet containers, and allows for compatibility with any portal server provider that adheres to to the standard.

The specification dictates the standards for deployment descriptors, deployment archives, base foundational classes, request processing, rendering, personalization, security, and so on. Given that the specification is the fundamental document for any portal development, it is a good idea to download it, read it, and use it as a reference. The documentation can be found on the Sun Microsystems web site: http://jcp.org/en/jsr/detail?id=168.

JSR-286 builds on JSR168, and is also called the Portlet 2.0 specification. As the portal platform matured, users started finding certain shortcomings in the existing portlet specification. These were specially in the areas of the portability of portlets, inter-portlet communication, events, filters, and resource serving. JBoss portal version 2.7.0 now provides support for JSR-286.

Portal URL

Portal URLs can become complicated as more pages are implemented. However, there are ways to set simpler alternate URLs, which make things a bit easier. It is important to note that portlets by themselves do not have a URL, and hence cannot be invoked directly. Portlets are part of a page and have an independent identity only from a browser perspective. Hence, they don't have their own URLs. Only portals and portal pages can be directly invoked; hence they can be invoked directly from browsers.

Each portal server can consist of several portals. Each one of these portals has its own URL. Similarly, each portal has several pages and each page has its own URL. To access a portal directly, we can use the portal name in the URL, which will then look like this: http(s)://server:port/portal/<portal name> .

Specifying only /portal takes us to the default portal. Similarly, to access a page on a given portal on the server, the URL we use is /portal/<portal name>/<page name> .

A Content Management System(CMS) serves content transparently like a portlet, hence doesn't impact the URL. We will discuss CMSs in more detail in Chapter 8.

Portlet modes

A portlet mode is a pointer to the type of function it will perform. There are three modes: VIEW, EDIT, and HELP. The mode indicates to a portlet the type of function it should perform and the associated content it should generate. When a portlet is invoked, the portlet is informed of the mode by the container. A portlet can have different modes depending on the function it needs to perform. There are three modes defined by the portlet specification, but a portal can extend them.

Let us look at these three modes in a bit more detail:

  1. VIEW: This mode generates content and markup reflecting the current state of the portlet. It can be considered to be a read-only kind of a mode, where the content is presented for a user to read and interact with — but not necessarily to change. This mode is used for creating screens for a user to interact without editing any content, or for generating static content.
  2. EDIT: Unlike VIEW mode, this mode allows a user to customize the behavior of the portlet. The portlet is supposed to provide options for the user to edit and customize the behavior of the portlet. This is typically used for creating and editing preferences.
  3. HELP: This mode provides the user with helpful information related to the portlets usage. The information can be comprehensive or content-sensitive. This mode is not always required to be provided.

Window states

Window states relate to the space allocated for a given portlet on a page. As each portlet generates its markup, it is important to manage the real-estate of the aggregated final portal page effectively. Window states play a major role in this. Portal window states can be compared with the state of windows in a Microsoft Windows environment. A window in Windows can be minimized, maximized, or can be set as normal. Portal window states behave the same way. When the portlet is invoked, just like the portlet mode, the window state is also provided to the portlet. The portlet, based on the state provided, decides the best way to effectively organize the content in the portlet that will eventually be served as a fragment. Again, a portlet can change the state programmatically.

The three states defined by the specification are:

  1. NORMAL: This indicates a shared space and requires a portlet to share the page with other portlets. The state is normal portal behavior, typically used when space is restricted and shared on the targeted device.
  2. MAXIMIZED: This state indicates a broader freedom for the portlet to display its content. A portlet with this state can either be the only portlet displayed on this page, or one with a disproportionately higher amount of screen real-estate in comparison with other portlets displayed on the same page. This state is typically used for rich content display.
  3. MINIMIZED: A kind of restricted viewing state that allows a portlet to display very little information, or in some cases, none at all.

A Hello World portlet

To understand the concepts better, let's dive right into writing the traditional "Hello World" code using a portlet.

By taking this simple example, we should be able to explore at least the major details of the specification, and their implication on real functioning code.

Portlet development environment

Portlet development, like web application development, requires a set of tools that help write code, build and successfully deploy our solution. The choice of the tools is very subjective, but typically, most of the developers prefer to use some kind of Integrated Development Environment(IDE) that encompasses all of their needs. However, if we want to stay agnostic of the IDE, command line developments can be a good option. At the least, the following set of tools is required to successfully create and deploy portlets:

  • Java Development Kit: Any version above 1.4.2 is a good fit
  • Ant or Maven: Either of these build tools can be leveraged to compile and build the application

Portlet package structure

Before we start with the code, let us build a context and briefly look at the overall structure of a deployable portlet archive.

Portlet archives are packaged asWAR(Web Archive) files, similar to archives in JEE applications. The content of our "Hello World" example as a WAR file will appear somewhat like this:

The directory structure is a standard structure for JEE projects. The structure can be created either manually or by using an IDE.

Portlet package structure

As you can see, the package consists of executable CLASS files, library JAR files and various deployment descriptors. For the purpose of simplicity, the example doesn't illustrate all of the scenarios, but a portlet WAR file can also consist of static content, resource bundles, JSPs, servlets, and so on.

The deployment descriptors illustrated in the figure are a combination of ones that are required by the JSR-286 specification, and others that are provided by JBoss to facilitate added features, easier configuration, and deployment on a JBoss portal platform. The JBoss-specific configurations provide significant value when deployed on a JBoss application server platform.

Writing the code

Now, let us look at the following code for our "Hello World" portlet:

package org.jboss.portlet.hello;
import java.io.IOException;
import java.io.PrintWriter;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletConfig;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.UnavailableException;
public class HelloWorld extends GenericPortlet {
public void init(PortletConfig portletConfig) throws UnavailableException, PortletException {
super.init(portletConfig);
}
public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {
// set return content type
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.println("<p>Hello World of Portals!</p>");
writer.close();
}
}

Let us parse through the code to understand it better.

public class HelloWorld extends GenericPortlet {

JSR-168 requires every portlet to implement the javax.portlet.Portlet interface. The Portlet interface is used by the container to invoke the portlets in the container. The GenericPortlet class is the default implementation of the Portlet interface, and provides an abstract class that portlets can then extend, and override its methods for various functions. The method processAction , for example, is used to handle requests, and the init and destroy methods are used to manage the life cycle of the portlet. However, the GenericPortlet class is significant for its implementation of the javax.portlet.Portlet render method, which sets the title of the portlet and invokes the doDispatch method, which in turn delegates the processing to one of the portlet mode methods, based on the mode indicated by the request.

public void init(PortletConfig portletConfig) throws UnavailableException, PortletException {
super.init(portletConfig);
}

This is an optional method, but it is good practice to use it, and a convenient way to initialize the portlet with any configuration information. In this case, we are not using a special configuration; instead, we default to the parent implementation.

public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {

For our example, we have used only the View mode for our portlet. Hence, we need to implement only the doView method of the GenericServlet class. When the portlet is invoked, the render method invokes this doView method through the doDispatch method.

// set return content type
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.println("<p>Hello World of Portals!</p>");

Now that we have dispatched the request to the appropriate view mode, it is time to implement functionality in the portlet and send a response back.

As our response needs to be served to a browser, we first set the response type, text/html in this case. We then invoke the PrintWriter to output our message Hello World of Portals! in our portlet window.

This code generates the portlet fragment that will be aggregated with other fragments and content to generate the final page. Hence, it is important that the content that gets generated has tags that are valid inside an HTML <body> tag.

With this simple piece of code implementing our portlet functionality, we are now ready to tell our portlet container about the portlet.

Application descriptors

The application descriptors are XML-based configuration file options that, among other things, indicate the properties and behavior of the portlet to the container.

The portlet specification, JSR-286, mandates some of these options, while the rest are specific configurations mandated by the portal server — JBoss portal in our case. There is also a standard J2EE web deployment descriptor.

The following screenshot shows the descriptors as part of the deployable WAR file that we saw earlier:

Application descriptorsportlet, hello world samplecode, writing

portlet.xml

portlet.xml is a mandatory deployment descriptor created as per the definitions in JSR-286 specification. All web related resources are defined in web.xml , as per the servlet specification, but all portal and portal server related resources are defined in portlet.xml .

<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns=http://java.sun.com/xml/ns/portlet/portlet-
app_2_0.xsd
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet /portlet-app_2_0.xsd http://java. sun.com/xml/ns/portlet/portlet- app_2_0.xsd"
version="2.0">
<portlet>
<portlet-name>SayHelloPortlet</portlet-name>
<portlet-class>org.test.portlet.hello.SayHelloPortlet</portlet- class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<portlet-info>
<title>Say Hello Portlet</title>
</portlet-info>
</portlet>
</portlet-app>

Let's look at the descriptor at bit more closely:

<portlet-name>SayHelloPortlet</portlet-name>

This sets the name of the portlet. This name is a basic reference, and not the actual class name of the portlet.

<portlet-class>org.test.portlet.hello.SayHelloPortlet</portlet-class>

This is the actual implementation class of the portlet, and the field requires the fully-qualified name of the portlet class.

<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>

This section describes the requested portlet modes for a given portlet, and the content types of the output to the render method.

By using the <mime-type> element, we are telling the render method that the portlet we are expecting to invoke will serve content of MIME type text/html , and the portlet mode for the portlet will be View . It is important to note that the portlet code should use the same mime type as the one declared here. We have used our output <mime-type> earlier in the code to be text/html , which matches with the type we have in the descriptor here.

<portlet-info>
<title>Say Hello Portlet</title>
</portlet-info>

This section declares the title of the portlet window. Our portlet window on the final output page will be called Say Hello Portlet .

The portal window renderer uses this code to assign the window title. We have an option to override and assign a different title name in our code, but assigning the title in the descriptor is a fairly convenient.

portlet-instances.xml

portlet-instances.xml is JBoss-specific descriptor that facilitates the creation of multiple instances of the portlet. Creating multiple instances and assigning a unique id to each instance, allows us to customize and tailor the behavior of the same original portlet for different needs.

<?xml version="1.0" standalone="yes"?>
<deployments>
<deployment>
<instance>
<instance-id>SayHelloPortletInstance</instance-id>
<portlet-ref>SayHelloPortlet</portlet-ref>
</instance>
</deployment>
</deployments>

In our case, we are going to create only a single instance. We will name it SayHelloPortletInstance , and tie it to our portlet by using the name given in the portlet.xml file. The <instance-id> element can have any name, but the <portlet-ref> element should match the name specified in the <portlet-name> element in portlet.xml .

This file is primarily declaring new instances and tying these instances to a pre-existing portlet declaration. The associations and respective specifications are done in a different descriptor, named *-object.xml , where * can be any name. The element <instance-id> now gains significance, and is used to tie the instance to the <instance-ref> element in *-object.xml , as we will see in the next descriptor.

sayhello-object.xml

The *-object.xml defines the structure and configuration of a portlet instance that has been created earlier. We can use this file to create and configure windows and pages, their behavior, and their structure. We have decided to call our file, sayhello-object.xml:

<?xml version="1.0" encoding="UTF-8"?>
<deployments>
<deployment>
<if-exists>overwrite</if-exists>
<parent-ref>default.default</parent-ref>
<window>
<window-name>SayHelloPortletWindow</window-name>
<content>
<content-type>portlet</content-type>
<content-uri>SayHelloPortletInstance</content-uri>
</content>
<region>center</region>
<height>1</height>
</window>
</deployment>
</deployments>

The descriptor allows for multiple deployments of portlets with distinct individual behaviors.

<if-exists>overwrite</if-exists>

This is used to manage copies of the object that may have already been instantiated. When we set the value to overwrite , we are telling the container to destroy any existing object and create a new one. If we set the value to the other allowed option, keep , the container retains any existing copy and creates a new instance. The decision to overwrite or keep an existing copy is purely a design call, and is based on the capacity of the server, performance requirements, and the complexity of the portlet.

<parent-ref>default.default</parent-ref>

This line basically indicates the location of our portlet, and stands for portal instance.portal page . In our case, by giving a value of default.default , we have decided to keep it simple and add the SayHelloPortlet to the default page of the default instance. We could very easily add the portlet to a new page that can be created using the approach outlined earlier in the chapter. In this case, the reference would change to default.newPage

<window-name>SayHelloPortletWindow</window-name>

This line assigns an arbitrary but unique window name to the portlet window.

<instance-ref>SayHelloPortletInstance</instance-ref>

This line references the portlet instance created in the portlet-instance.xml file. Again, the value of this element should be the same as the <instance-id> element in portlet-instance.xml

<region>center</region>
<height>1</height>

These lines specify the location and dimension of the portlet window. There are various configuration options for these elements, but in our case, we are indicating that the portlet should appear in the center of the page layout as described by identifier in the layout descriptor, and should be second portlet from the top as indicated by height 1 . Height "0" is the first portlet from the top in a column. We will see later how the location and the order can be changed using the admin console.

The inter-relationship of these descriptors is as follows:

sayhello-object.xml

The <portlet-name> in portlet.xml is used to create an instance using <instance-id> in portlet-instances.xml . The properties of this instance is defined in *-object.xml .

web.xml

web.xml is a standard web deployment descriptor mandated by the servlet specification, and any web resource configurations go in this file.

Due to the simplicity of our example, in our case, this file is empty.

Building the application

Now that we have our code and descriptors in place, let us start building our deployable artifact, and plan for its eventual deployment on the server.

The source code associated with this chapter provides the necessary build scripts to compile and create deployable artifacts. There are multiple ways of creating the WAR file — the build script uses one such technique. We will create our portlet source file under the src/main directory, and place our deployment descriptors under src/resource/sayhelloportlet-war . The libraries required for building will go under the lib directory.

The script will first compile the source, create a JAR file of the compiled binaries, and copy this JAR file to the src/resource/sayhelloportlet-war/WEB-INF/lib directory. It will then package the contents of the src/resource/sayhelloportlet-war directory into a WAR file.

The screen output will look like the following screenshot:

Building the application

Although we have packaged the portlet classes as a library, it is not necessary to do so. As with a J2EE application, the CLASS file tree can be copied as it is, below the WEB-INF/classes directory.

Deploying the application

We are now ready to deploy the WAR file to the portal server. We can deploy the file in a couple of ways:

  • Deploy as a single WAR file archive
  • Deploy in an exploded mode where the contents retain their directory structure

An exploded deployment has significant value when we need more control over the content. However, for our purposes, we will use the WAR file deployment option.

To deploy the WAR file, all we need to do is drop the file into the deploy directory of the JBoss application server (AS or EAP). This can be done as a hot deploy, while the server is running.

For bundle installs, the deploy directory is $JBOSS_PORTAL_HOME/server/default/deploy .

The output on the console, indicating deployment activity after we drop the WAR file, is as follows:

 03:27:42,810 INFO [TomcatDeployer] deploy, ctxPath=/sayhelloportlet, warUrl=.../tmp/deploy/tmp26738sayhelloportlet-exp.war/

Accessing the page and portal URL

Once we have successfully deployed the application, we can then go to the default URL for JBoss server: http://localhost:8080/portal .

The default URL and page corresponds to our default.default location setting earlier in the descriptor.

We can now see our portal, displayed in the center of the page, in View mode:

Accessing the page and portal URL

Minor changes to content can be done directly on the tree without redeploying the whole archive. To trigger a redeploy, we just need to update the timestamp on the web.xml file by touching it. A web.xml file with a timestamp later than the last deployment will trigger the JBoss portal to deploy the application again. It is worth noting here that this time the deployment takes place in an exploded format as there is no WAR file involved.

Summary

In this chapter, we went a bit deeper into JBoss portal server and tried to understand portlets better by creating a simple portlet application. We went through the complete life cycle, from code creation to deployment. Hopefully, this overview tour gave you a good idea of the major components that are required to create a functional portlet.

This introductory application barely touched the true potential of the server and the choices it offers. In the coming chapters, we will explore the potential of the portal further; we will develop custom portlets, and move into some real world scenarios by introducing some newer patterns, choices, and techniques.

Nothing clears things up better than a demo application. In the next chapter, we will also introduce the sample application — a corporate Intranet portal called "MyCompany Portal", which we will use throughout this book. As we learn new concepts, we will add them to the portal, and it should hopefully jump-start our enterprise portal development.

主站蜘蛛池模板: 东乡族自治县| 开化县| 洛宁县| 民权县| 盘山县| 晴隆县| 高碑店市| 寿光市| 华亭县| 静海县| 谷城县| 桑植县| 遂平县| 崇明县| 海口市| 原平市| 博野县| 东港市| 呼伦贝尔市| 宜章县| 水城县| 米易县| 重庆市| 宜章县| 怀宁县| 高阳县| 吐鲁番市| 延边| 深泽县| 达日县| 京山县| 临江市| 安西县| 焦作市| 砀山县| 汉中市| 穆棱市| 西林县| 吉林省| 沁阳市| 宜州市|