> developer > dnu > courses > portal overview page 16
Novell Portal Services Overview and Gadget Development
April 2003
DeveloperNet University Course
Reader Rating    from ratings rate this article
View an eBook Version of this course - LARGE FILE! Send this page to a friend

Gadget Development Basics

With a basic understanding of HTML, CSS, XML, DTD, and XSL, we are ready to dive into a study of Gadget Development Basics. This section will be followed by a more advanced Gadget Development discussion.

In this part of the course, we will cover the following topics:

  • What is a Gadget?

  • Gadget Architecture and Lifecycle

  • Calling Gadgets

  • Sending Data

  • Configuration

  • Available Settings

  • Secret Store

NPS utilizes the following technologies:

  • Java (including "server-side" concepts)

  • HTTP

  • XML

  • XSL

Knowledge in these areas is required for successful NPS gadget development. However, the following two technologies are good to know, but are optional:

  • HTML

  • JavaScript, CSS, etc.

What is a Gadget?

A Gadget is an NPS application that is used to interface with a resource. A gadget consists of Java code and XSL stylesheet(s). It sits inside the NPS framework and outputs XML. It may receive user input from a browser. Also, it may interface with one or more external data sources such as applications, databases, and websites.

Figure 26 shows the data flow for a Gadget.

Figure 26: Gadget Data Flow.

A typical gadget consists of the following pieces:

  • One or more Java classes that implement the gadget's logic.

  • One or more of either XSLT stylesheet files, which transform the XML data generated by the gadget into HTML for display, or Java Server Pages (JSPs), which operate as user interface templates.

  • Zero or more JAR files required by the gadget to access its external data sources.

  • An XML file (AvailableSettings.xml) that describes the gadget's configurable settings, and related resource DTD files containing translated configuration strings.

These pieces are normally packaged together into a single Novell portal gadget (.npg) file for deployment to the portal. The GDK provides a "Packager" utility for this purpose.

Novell provides a software developer kit for writing gadgets. This kit includes the following:

  • Gadget Runner

  • Documentation

  • Sample Code

Best of all, it is free. You can download the SDK at: http://developer.novell.com/ndk/dex.htm

How a Gadget Works

When a user first brings up a portal page in the browser, the portal creates a new instance of each gadget on that page and assigns it a unique ID. The gadget instance maintains state information about the user's session and continues to exist until the servlet engine drops the session. The portal then calls the init(String[] args) method of each gadget.

Gadgets that Use XSLT Stylesheets.  For gadgets that use XSLT stylesheets to generate their interface, after the portal initializes the gadget, the portal calls the getData and getStylesheetAndResourceSets methods of the gadget instance to get the XML data and the XSLT stylesheet URL. The portal renders the gadget display by using the stylesheet to transform the XML data into HTML or some other format. Whenever the portal needs to regenerate the display, it again calls these two methods of this same gadget instance.

To see an example of how this works, open the HelloWorld.java sample gadget file. This is the most basic type of gadget, one that simply returns XML to the portal and performs no processing. To accomplish this, the HelloWorld class extends the com.novell.nps.gadgetManager.BaseGadgetInstance class from which it inherits the getData method. HelloWorld then overrides getData and returns a static XML page to the out stream.

If you look at the getData method, you notice that the method returns the following three lines to the portal (using convenience methods of the com.novell.nps.gadgetManager.XmlUtil class):


<HelloWorld>
<HELLODATA>Hello World!</HELLODATA>
</HelloWorld>

The portal receives this XML data, then applies the HelloWorld stylesheet. Since HelloWorld does not override getStylesheetAndResourceSets, the portal loads the default stylesheet specified in com.novell.nps.gadgetManager.BaseGadgetInstance, which is main.xsl. This XSL stylesheet uses only three lines to format the data:


<xsl:template match="HelloWorld">
<xsl:value-of select='HELLODATA'/>
</xsl:template>

The portal replaces <xsl:value-of select='HELLODATA'/> with the data in the HELLODATA tags, Hello World!, according to XSLT rules, and displays the data in the portal.

Gadgets that Use JSPs.  For gadgets that use JSPs, the portal calls the execute(HttpServletRequest req) method of the gadget, which sets the initial JSP by calling setModuleStylesheet(String moduleStylesheet). The gadget also stores data in a java.util.Properties object called m_JSPDataObject. The portal uses this initial JSP and the data stored in m_JSPDataObject to render the initial user interface for the gadget. If the user interface must change (in response to user interaction, for example), other JSPs are required for each UI change (see Processing Data ).

The JSP is a file on the iManager server that operates as a template for the gadget user interface. It contains dynamic code enclosed in special tags interspersed with static HTML markup. The dynamic portions can cause variable data to be inserted, such as data from m_JSPDataObject. They can also insert the results of other processing.

Installing the Gadget Runner.  The Novell Gadget Developer Kit (GDK), available on Novell's Developer web page (http://www.developer.novell.com) contains a tool called the Gadget Runner. This is optional for this course, but may make your life easier in completing the labs at the end of this course. If you opt to use the Gadget Runner, make sure you have downloaded the GDK, and follow the instructions below.

This section shows you how to use the Gadget Runner that is bundled with the GDK. The Gadget Runner is a customized version of the Tomcat 4.x servlet container that runs a developer version of Novell exteNd Director, Standard Edition. The Gadget Runner displays a single gadget at a time on your development workstation, allowing you to easily develop and test gadgets before loading them on a Novell exteNd Director server. The Gadget Runner is compatible with the dynamic class loading feature of Tomcat 4, which allows you to change your gadget code, recompile, and test your changes without restarting the Gadget Runner.

The Gadget Runner is installed with the GDK. To run your gadgets in the Gagdet Runner:

  1. Copy your stylesheets to the GDK_HOME\tomcat\webapps\nps\portal\gadgets\gadgetclassname\skins\default\devices\default folder, where GDK_HOME represents the directory where you installed the GDK files and gadgetclassname represents the fully-qualified name the main class of your gadget.

  2. Copy the compiled class files of the gadget to the directory GDK_HOME\tomcat\webapps\nps\WEB-INF\classes. If you have archived your gadget in a JAR file, copy it to the GDK_HOME\tomcat\webapps\nps\WEB-INF\lib directory.

  3. Run GDK_HOME\gadgetRunner.bat to start the Gadget Runner.

  4. The Gadget Runner executes in a separate window, where a number of status messages appear during initialization. When the Gadget Runner is running, open your Web browser and browse to http://localhost:8080/nps/index.html. You know the Gadget Runner is running when you see a message similar to the following:

INFO: Jk running ID=0 time=16/78 config=C:\gdk\.\tomcat\conf\jk2.properties

  1. Select the gadget you want to run from the drop-down list and click the Run button.

  2. To stop the Gadget Runner, execute GDK_HOME\tomcat\bin\shutdown.bat.

You might also be able to start the Gadget Runner from within your IDE, but this depends on the type of IDE you are using. See "Setting Up a Development Environment" in the GDK, for more information.

Creating a Gadget.  This course is designed to give you the basics for Gadget creation on any of the platforms supported by the GDK. However, when you download the GDK, detailed documentation is provided for using the following IDEs:

  • Borland JBuilder 4/5/6/7

  • Symantec/WebGain Visual Café 4

Of course the JDK command-line environment will work also.

Figure 27 shows the basic Gadget class diagram.

Basic Gadget Class Diagram.

Figure 27: Basic Gadget Class Diagram.

When you create a Gadget, you can either implement a GadgetInstance or extend the BaseGadgetInstance. For example:


public class MyGadget extends
com.novell.nps.gadgetManager.BaseGadgetInstance
{
//...
}

Gadget Lifecycle.  When implementing a Gadget, there are several important methods to implement. Three of these are:

  • public void init()

  • public void getData() throws GadgetInstanceException

  • public void processRequest()

Let's look at each of these in a little more detail,

init() This method performs instance initialization for the gadget.

getData() This method can override to return an XML page to the output stream. It is also the default method to return data. It is always called unless there is a state change, which we will talk about later in the course.

processRequest() Many gadgets need to receive and respond to user input such as forms or request parameters embedded in the URL query string. The portal distinguishes the requests that need processing by the presence of the GI_ID parameter in the URL or as a hidden field in a POSTed form. The portal then uses the specified gadget instance ID to identify the target GadgetInstance object and calls its processRequest method to allow it to process the request.

Normally, the call to processRequest is followed by a call to getData to get the updated display, which is presumably updated to reflect the request just processed. Sometimes, however, a gadget needs complete control over the HTTP response that is sent back to the browser. For example, the gadget might need to send a redirect or a download file. This can be achieved by setting the parameter CUSTOM_CONTENT_TYPE=yes in the request URL. Setting this parameter causes the portal to call another version of processRequest that takes HttpServletResponse as an additional argument and requires the gadget to generate the entire response. It also prevents the portal from calling getData.

A gadget often needs to process several different kinds of requests, and needs a separate method for each request, rather than funneling everything through processRequest. The portal supports this arrangement through "actions." The gadget instance needs to have a separate handler method with the signature void onXXXAction(HttpServletRequest) throws GadgetInstanceException, where XXX is the name of the action.

The GadgetInstance object must implement the addActionListener method to allow it--and possibly others--to register for the actions they want to handle. Each listener must have the appropriate onXXXAction method. The GadgetInstance object must also implement the handleAction method, which is called by the portal instead of the processRequest method. The handleAction method calls the onXXXAction method of each registered listener for action XXX. Fortunately, if you extend BaseGadgetInstance, these methods are implemented for you and all you have to do is write the onXXXAction methods and invoke addActionListener to add your gadget instance as a listener for each action.

An action is specified in the request using the NPAction=XXX parameter, where XXX is the name of the action. If the NPAction parameter is not present, the getAction method is called to allow the gadget to specify the action. If no action is specified, the portal calls the processRequest method; otherwise, it calls the handleAction method, which in turn calls the onXXXAction method for each registered listener (normally only the gadget instance itself).

processRequest is followed by a call to getData. However, there is an exception: parameter CUSTOM_CONTENT_TYPE=yes passed in the request URL. Also allows some processing to take place before getData() is called. It is also a good place to have State changes (setState()) as a result of data in the request header. (We will talk more about this later in the course.)

Calling Gadgets.  Referring to Figure 28, each gadget is called by the GadgetManager and the XML output from the gadget is itself "wrapped" with XML from the GadgetMaseter.

Calling Gadgets.

Figure 28: Calling Gadgets.

This "wrapping" tags on the GI_ID to the XML output from the Gadget. Here is an example:


<Gadget title= "MyGadget"id="3"...>
... XML output from gadget ...
<Gadget>

Gadgets can take advantage of this "wrapping" to access the GI_ID in their stylesheet (main.xsl). For example, if we are one level deep in the DOM tree of the gadget XML, then the corresponding XSL document can access the GI_ID attribute by traversing two levels up the tree.

<xsl:value-of select=../../@id/>

How a Gadget Works

When a user first brings up a portal page in the browser, the portal creates a new instance of each gadget on that page and assigns it a unique ID. The gadget instance maintains state information about the user's session and continues to exist until the servlet engine drops the session. The portal then calls the init(String[] args) method of each gadget.

Gadgets that Use XSLT Stylesheets

For gadgets that use XSLT stylesheets to generate their interface, after the portal initializes the gadget, the portal calls the getData and getStylesheetAndResourceSets methods of the gadget instance to get the XML data and the XSLT stylesheet URL. The portal renders the gadget display by using the stylesheet to transform the XML data into HTML or some other format. Whenever the portal needs to regenerate the display, it again calls these two methods of this same gadget instance.

To see an example of how this works, open the HelloWorld.java sample gadget file. This is the most basic type of gadget, one that simply returns XML to the portal and performs no processing. To accomplish this, the HelloWorld class extends the com.novell.nps.gadgetManager.BaseGadgetInstance class from which it inherits the getData method. HelloWorld then overrides getData and returns a static XML page to the out stream.

If you look at the getData method, you notice that the method returns the following three lines to the portal (using convenience methods of the com.novell.nps.gadgetManager.XmlUtil class):


<HelloWorld>
<HELLODATA>Hello World!</HELLODATA>
</HelloWorld>

The portal receives this XML data, then applies the HelloWorld stylesheet. Since HelloWorld does not override getStylesheetAndResourceSets, the portal loads the default stylesheet specified in com.novell.nps.gadgetManager.BaseGadgetInstance, which is main.xsl. This XSL stylesheet uses only three lines to format the data:


<xsl: stylesheet version="1.0" xmlns:xsl=httP://www.w3.org/1999/XSL/Transform>
<xsl:template match="HelloWorld">
<!--Display the value of HELLODATA element on the screen -->
<xsl:value-of select='HELLODATA'/>
</xsl:template>
</xsl:stylesheet>

The portal replaces <xsl:value-of select='HELLODATA'/> with the data in the HELLODATA tags, Hello World!, according to XSLT rules, and displays the data in the portal. For the end result, see Figure 29.

HelloWorld on Screen.

Figure 29: HelloWorld on Screen.

Gadgets that Use JSPs

For gadgets that use JSPs, the portal calls the execute(HttpServletRequest req) method of the gadget, which sets the initial JSP by calling setModuleStylesheet(String moduleStylesheet). The gadget also stores data in a java.util.Properties object called m_JSPDataObject. The portal uses this initial JSP and the data stored in m_JSPDataObject to render the initial user interface for the gadget. If the user interface must change, such as in response to user interaction, other JSPs are required for each UI change (see Processing Data ).

The JSP is a file on the iManager server that operates as a template for the gadget user interface. It contains dynamic code enclosed in special tags interspersed with static HTML markup. The dynamic portions can cause variable data to be inserted, such as data from m_JSPDataObject. They can also insert the results of other processing.

Sending Data

There is a 100% chance that you will want to send data between your gadget and browser. When you do this, how do you keep track of which gadget you are currently communicating with?

  • To distinguish between gadgets on a page, each has a unique gadget instance ID called a GI_ID.

  • When sending data from the browser to the gadget this ID should be included in the URL.

  • The portal then calls the processRequest method of the required gadget.

Figure 30 illustrates this data transfer:

Data Transfer from Browser to Gadget.

Figure 30: Data Transfer from Browser to Gadget.

The following is a short snippet of code illustrating how to use the processRequest method:


private static String URL = http://www.novell.com;
.
.
.
public void processRequest(HttpServletRequest req)throws GadgetInstanceException
{
// Check each setting to see if we need to update the configuration
String settingl = req.getParameter("NEWURL");
if(settingl != null && !settingl.equals(""))
{
URL = settingl;
}
}

SecretStore

There is a very good chance that you will want to have some sort of security to allow users to authenticate to a portion of your web site. Novell has SecretStore technology that will allow you to do this very thing, Figure 31 shows this process:

SecretStore Authentication Process.

Figure 31: SecretStore Authentication Process.

This course is not designed to be a security course, but instead shows you how to incorporate authentication technology into your Gadget. For more information on Novell's SecretStore technology, see: http://developer.novell.com/ndk/nmas.htm

The following code illustrates this:


public void getData( HttpServletRequest req, BufferedWriter out, Document domTree )
{
BasicCredentials credentials = null;
try
{
// Get user's credentials from either Secret Store or the request.
// Will throw an Exception if no credentials are found.
// Will write credentials to Secret Store if specified in request.
Debug.log( SOURCE, "Get/Store credentials from/to Secret Store");
credentials = getUserInformation( reg, CREDENTIAL_TAG, null);
}
catch( gadgetInstanceException gie)
{
// Solicit user's credentials by presenting login form.
// The subsequent call to getData will save them in Secret Store.
Debug.log( SOURCE,"Display Login form.");
displayGetCredentials( out, PROMPT_TAG, false, false);
return;
}

Figure 32 shows the final login page:

Login Page.

Figure 32: Login Page.

Previous Contents Next
download sample file