PSML Storage and Access
Victor had analyzed Jetspeed fairly thoroughly at this point and had an idea of what classes might be available to use in the Sonic Systems project. Not many, he decided, but enough to save a little time and to give an overall framework for how the portal should look and feel. A few classes were available to read and write PSML, and they would return object representations of the XML elements. But to go any further, Victor needed to understand PSML.
The first lesson he learned was that PSML was used in two different file types: XREG and PSML files. The former were used to store portlets, skins, and other information in XML registries. The latter were used to store user information. Many of the elements were the same, though the purpose and organization of each was quite different. Victor chose to analyze the registries first.
PSML Registries
He simplified a registry template in order to dissect it:
<?xml version="1.0" encoding="UTF-8"?> <registry> <portlet-entry name="HelloJSP" hidden="false" type="ref" parent="JSP" application="false"> <meta-info> <title>HelloJSP</title> <description>Simple JSP Portlet Example</description> </meta-info> <parameter name="template" value="hello.jsp" hidden="false"/> <media-type ref="html"/> </portlet-entry> </registry>
After the XML declaration, the registry was wrapped in the <registry> root element. Within this element were a number of <portlet-entry> elements (only one was in his sample registry). These represented portlets within the system. A number of attributes defined the name of the portlet, if it was hidden, what type it was (they all seemed to be ref), a parent (usually JSP, HTML or class), and whether it was an application. Victor wasn't entirely sure what all of these represented, nor if they would need to use them.
Additionally, the <meta-info> element contained a title and a descriptionboth separate elementsdefining the portlet. This is most likely what a user would see regarding a portlet. The <parameter> element was a multipurpose element that would set basic parameters and define whether they were hidden or not. In this sample, a hidden parameter called template was set to "hello.jsp". Finally, a <media-type> element has a ref attribute set to either html or wml; some had two elements. This defined whether the application was compatible with Web browsers (HTML) or wireless devices (WML).
It all seemed to make sense, but there was definitely more here than they would need, at least in this phase. The wireless compatibility, for one thing, wouldn't be necessary. Some of the attributes didn't have a lot of meaning either, such as hidden; these would most likely be ignored.
There were other registries that were similar: one for controllers, one for controls, one for skins, and one for media types. They were all structured similarly, containing entries called <skin-entry> and <media-type-entry>. They had a few different elements and attributes, but these were fairly intuitive. For example, the skins registry contained <property> elements that stored a name and hexadecimal or class values. These were likely styles and colors to control the output of the portal interface.
The purpose of this exercise was to plan how this aspect of the application would be used and also programmatically implemented. Because these were fairly straightforward documents, Victor felt that Macromedia Cold-Fusion's XML capabilities could be used to access information within registries. The portlet registries were the only ones that Victor would really use extensively, at least for now. With multiple portlet registries, different types of users could be associated with different registries. The registry a user had access to would define the portlets available to that user. For example, an administrative user would be able to access portlets to edit users and groups, but an ordinary user wouldn't. An anonymous user would only be able to access very public portletspossibly only HTML-based portlets.
The skins, media type, and controller registries would be kept in place. Developers of related functions could use them if necessary, or ignore them if not.
User PSML Files
Next, Victor analyzed a user's PSML file. To start, all of the PSML files were stored in a protected directory called PSML. Within this directory, there were a number of subdirectories. Some seemed to make sense, such as users and groups, while others, such as anon and turbine, made less sense. The directories within these directories were even more numerous and ultimately a bit convoluted, but did display some sense of logic. Figure I-3.1 shows this directory structure.
Figure I-3.1 The users' PSML files are stored in a logical, if complex, directory structure.
Ultimately, Victor decided to simplify the PSML directory structure. There would be two main directories: groups and users. The groups directory would hold the default PSML profiles for a group, and users would contain all the PSML files for each user. Only HTML and nonlocalized portlets would be necessary to support, so many of the subdirectories (such as wml, for wireless portlets, and es for Spanish portlets) wouldn't need to be created.
As for the contents of the actual default.psml file, Victor looked at these (as well as the Jetspeed documentation) and made a note of the XML structure. Every file had the same basic elements:
<?xml version="1.0" encoding="iso-8859-1"?> <portlets xmlns="http://xml.apache.org/jetspeed/ 2000/psml"> <control name="TabControl"/> <controller name="CardPortletController"> <parameter name="parameter" value="pane"/> </controller> <skin name="orange-grey"/> <portlets> <controller name="RowColumnPortletController"> <parameter name="sizes" value="66%,34%"/> <parameter name="mode" value="row"/> </controller> <metainfo> <title>Home Page</title> </metainfo> <portlets> <controller name="RowColumnPortletController"/> <entry parent="JetspeedContent"/> </portlets> <portlets> <controller name="RowColumnPortletController"/> <entry parent="Jetspeed"/> <entry parent="Welcome"/> </portlets> </portlets> </portlets>
The root element was the <portlets> element, Victor noted, and that <portlets> was also a child element; usually (though not in this example) multiple child <portlets> existed in each document. The names in the <control> elements matched items in the control registry. This was also true for all instances of both controller and skin elements, in regards to their registries. So these user files just stored metadata for each user. The actual data was stored in the registry, and these files simply served as lookups to store user information.
The <portlets> element was also a grandchild of the main <portlets> element. However, the <portlets> at this level had only a <controller> element and an <entry> element. It seemed that this could keep recursing. The controller defined how information would be displayed; the entries would then refer to the actual portlets (in one of the portlet registries) to render. This meant that a controller, say one that handled navigation, could actually have other controllers within it, such as a sub-navigation controller.
This was way too confusing for what they needed to do, Victor thought. Recursing wasn't necessary. Everything in this application was controlled using controllers, including the navigation. Victor decided that he would separate the navigation from the actual controllers. However, controllers would still be created to handle how the portlets would be displayedfor example, whether a page would have one, two, or three columns of portlets. And having multiple skins was too confusing; a single skin would be allowed for each user.
The bottom line, Victor concluded, was that PSML was a good way to store information, but there was no need to use everything it offered, or even to use it in the same way that Jetspeed used it. Of course, much of this would be determined by the tools.
Jetspeed API
Now that Victor had a good idea of what was available, he referred back to the API documents. He needed to find items that could help him read and write PSML files. However, what he discovered was that many of the classes developed for this purpose required that complex objects be passed to them. For example, the org.apache.jetspeed.services.psmlmanager class seemed to be set up primarily to edit users' PSML documents. However, the create() method, which created a new PSMLDocument object, needed to be passed an object of type org.apache.jetspeed.om.profile, or Profile. This is a common and useful technique in Java because all the information stored in the object passed can then be utilized by the method. However, the complexity of creating the Profile object simply added more to the process and would defeat the purpose of using Jetspeedto speed up and simplify development. Eventually, Victor discovered that the org.apache.jetspeed.xml.api.portletmarkup.Portlets class contained a number of low-level utilities that could be used for reading these documents. Bingo!