April 2008
« June 2008 | Main | March 2008 »Best Practices for Links in WebLogic Portal
Friday, April 04, 2008
Introduction
A question that comes up from time to time is what is the best practice with respect to the format of URLs within links in a portlets. By links, we mean both links used in anchor tags and used when accessing resources, for example the src attribute in the img tag.
First a little background, there are three main styles used to construct a link in a web application. These are:
- Absolute. This is a link that contains the full URL including the scheme (http or https), domain and port. For example, http://www.yahoo.com/finance would be an example of an absolute link.
- Relative. This is a link that is relative to the current document, for example ../images/someimage.jpg.
- Root Relative. This is a variant of the relative style link, in this case the link is relative to the root of the web application. Links of this nature always start with a slash followed by the application's context root. An example of a root relative link would be /application/framework/skins/myskin/images/someimage.jpg where application is the context root of the web application.
Portal Links
When constructing links in WebLogic Portal the best practice in my opinion is to use root relative links. The problem with absolute links is that it can be difficult for the application to accurately determine what the front end scheme, domain and port is given the amount of infrastructure that could potentially be sitting between the WebLogic Portal server and the user's browser. This gets even more complicated to manage if users will access the site from different front ends. A root relative link eliminates this issue by leaving it to the browser to determine these parameters based on the URL of the current document.
Relative links have a different issue in that they are incompatible between a file mode (.portal) portal and a streaming desktop portal. In a file mode portal, the URL will look something like http://www.someserver.com/myapplication/myportal.portal. Therefore any relative links in the portal will be resolved against the http://www.someserver.com/myapplication address. Thus if you have a relative link of images/someimage.jpg the actual URL used by the browser to retrieve it is http://www.someserver.com/myapplication/images/someimage.jpg.
Now if you take that same code and run it in a streaming desktop you have a problem. A streaming desktop has a URL like http://www.someserver.com/myapplication/portal/desktop which means the previous link now gets resolved as http://www.someserver.com/myapplication/portal/desktop/images/someimage.jpg. Of course this will fail since the image cannot be found in that location.
Root relative links solve both issues rather neatly and are thus the best choice to use. One thing to keep in mind is that if you are constructing the URL manually make sure not to hard code the context path in the link. You should use the request.getContextPath() method or an equivalent so that if the context path of the application changes none of your links will break.
There is one minor exception to this and that is with respect to CSS files. A CSS file will often contain a link to an image, for example a background-url element that specifies the image to use for the background of an object. I prefer making these links relative instead of root relative for the simple reason that there is no easy way to dynamically insert the application's context path into the CSS. This is OK because the issue of file mode versus streaming mode portal URLs does not apply here, links in a CSS file are resolved according to the location of the CSS file not the document that referenced the CSS file. As a result the URL of the portal has no bearing on resolving image links within a CSS file.
beehive-url-template-config.xml
Staying with this discussion, those of you who have worked with Portal 9.2 or 10 know there is a file called beehive-url-template-config.xml that controls how Portal rewrites links. This template by default specifies that URLs be created as absolute links. This works well in simpler environents, but in more complex environments problems can occur when the Portal cannot properly deduce the correct parameters to use when building the absolute elements of the URL.
One option to work around this issue would be to hard-wire elements of the template file. For example, instead of using the {scheme} parameter simply hardwire in https instead if the portal is always accessed through SSL. This can work, but requires the creation of deployment plans when the absolute elements vary from environment to environment, for example differences between a test and a production environment. It can also be problematic when the portal can be accessed through multiple front ends, for example internal users go through a non-secure URL while external users go through a secure URL.
An alternative solution is to simply use a root relative version of the beehive-url-template-config.xml file. In this way we leave it to the browser to determine the scheme, domain and port based on the current URL. To make this change, switch to the merged projects view of your portal web application. Under WEB-INF, right click the beehive-url-template-config.xml file and select copy to project. Now edit the file to remove the scheme, domain and port from all entries. Here is an example of what the file looks like after the changes.
<?xml version="1.0" encoding="UTF-8"?>
<url-template-config xmlns="http://beehive.apache.org/netui/2004/server/url-template-config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.bea.com/servers/weblogic/url-template-config/8.0 url-template-config.xsd">
<url-template>
<name>default</name>
<value>
/{url:path}?{url:queryString}{url:currentPage}
</value>
</url-template>
<url-template>
<name>default-complete</name>
<value>
/{url:prefix}/{url:path}?{url:queryString}{url:currentPage}
</value>
</url-template>
<url-template>
<name>portlet-default</name>
<value>
/{url:path}?{url:queryString}{url:currentPage}
</value>
</url-template>
<url-template>
<name>portlet-secure-default</name>
<value>
/{url:path}?{url:queryString}{url:currentPage}
</value>
</url-template>
<url-template>
<name>portlet-action</name>
<value>
/{url:path}?{url:queryString}{url:currentPage}
</value>
</url-template>
<url-template>
<name>portlet-secure-action</name>
<value>
/{url:path}?{url:queryString}{url:currentPage}
</value>
</url-template>
<url-template>
<name>portlet-resource</name>
<value>
/{url:path}?{url:queryString}
</value>
</url-template>
<url-template>
<name>portlet-secure-resource</name>
<value>
/{url:path}?{url:queryString}
</value>
</url-template>
<!-- Templates for consuming remote portlets -->
<url-template>
<name>wsrp-default</name>
<value>
/{url:path}?{url:queryString}&wsrp-urlType={wsrp-urlType}&wsrp-url={wsrp-url}&wsrp-requiresRewrite={wsrp-requiresRewrite}&wsrp-navigationalState={wsrp-navigationalState}&wsrp-interactionState={wsrp-interactionState}&wsrp-mode={wsrp-mode}&wsrp-windowState={wsrp-windowState}&{weblogic-passthru}
</value>
</url-template>
<url-template>
<name>wsrp-secureDefault</name>
<value>
/{url:path}?{url:queryString}&wsrp-urlType={wsrp-urlType}&wsrp-url={wsrp-url}&wsrp-requiresRewrite={wsrp-requiresRewrite}&wsrp-navigationalState={wsrp-navigationalState}&wsrp-interactionState={wsrp-interactionState}&wsrp-mode={wsrp-mode}&wsrp-windowState={wsrp-windowState}&{weblogic-passthru}
</value>
</url-template>
<url-template>
<name>wsrp-blockingAction</name>
<value>
/{url:path}?{url:queryString}&wsrp-urlType=blockingAction&wsrp-url={wsrp-url}&wsrp-requiresRewrite={wsrp-requiresRewrite}&wsrp-navigationalState={wsrp-navigationalState}&wsrp-interactionState={wsrp-interactionState}&wsrp-mode={wsrp-mode}&wsrp-windowState={wsrp-windowState}&{weblogic-passthru}
</value>
</url-template>
<url-template>
<name>wsrp-render</name>
<value>
/{url:path}?{url:queryString}&wsrp-urlType=render&wsrp-url={wsrp-url}&wsrp-requiresRewrite={wsrp-requiresRewrite}&wsrp-navigationalState={wsrp-navigationalState}&wsrp-interactionState={wsrp-interactionState}&wsrp-mode={wsrp-mode}&wsrp-windowState={wsrp-windowState}&{weblogic-passthru}
</value>
</url-template>
<url-template>
<name>wsrp-resource</name>
<value>
/{url:path}/resource?{url:queryString}&wsrp-urlType=resource&wsrp-url={wsrp-url}&wsrp-requiresRewrite={wsrp-requiresRewrite}&{weblogic-passthru}
</value>
</url-template>
<url-template>
<name>wsrp-secureBlockingAction</name>
<value>
/{url:path}?{url:queryString}&wsrp-urlType=blockingAction&wsrp-secureURL=true&wsrp-url={wsrp-url}&wsrp-requiresRewrite={wsrp-requiresRewrite}&wsrp-navigationalState={wsrp-navigationalState}&wsrp-interactionState={wsrp-interactionState}&wsrp-mode={wsrp-mode}&wsrp-windowState={wsrp-windowState}&{weblogic-passthru}
</value>
</url-template>
<url-template>
<name>wsrp-secureRender</name>
<value>
/{url:path}?{url:queryString}&wsrp-urlType=render&wsrp-secureURL=true&wsrp-url={wsrp-url}&wsrp-requiresRewrite={wsrp-requiresRewrite}&wsrp-navigationalState={wsrp-navigationalState}&wsrp-interactionState={wsrp-interactionState}&wsrp-mode={wsrp-mode}&wsrp-windowState={wsrp-windowState}&{weblogic-passthru}
</value>
</url-template>
<url-template>
<name>wsrp-secureResource</name>
<value>
/{url:path}/resource?{url:queryString}&wsrp-urlType=resource&wsrp-secureURL=true&wsrp-url={wsrp-url}&wsrp-requiresRewrite={wsrp-requiresRewrite}&{weblogic-passthru}
</value>
</url-template>
</url-template-config>
Posted by Gerald Nunn at 10:17 AM | Categories: WebLogic | | | Permalink
WebLogic Portal Analytics API
Tuesday, April 01, 2008
Aqualogic Analytics provides a great way to for administrators to collect, view and report on metrics with regards to WebLogic Portal usage, now the API it uses to collect these metrics from WLP has been made public in 10.2. Here is an example of how to use this new API to create a very basic view of aggregated analytics suitable for non-production use.
WebLogic Portal has supported the collection of analytics for a few versions now through the Aqualogic Analytics product. A new feature in 10.2 though is that the way these analytics are collected are now exposed through a public API in the com.bea.netuix.servlets.controls.analytics package. While this has just been documented, the APIs are available in earlier versions of WLP 10 as well.
While someone could go hog wild and provide the equivalent functionality of the Aqualogic Analytics package, there are a host of other useful functions that can be driven off of these metrics. For example, the metrics could be used to monitor SLAs and trigger a warning if a specific portlet's response times starts to exceed agreed upon SLAs. Another use case would be to provide debug information at development time to allow developers to get an idea of the relative performance level of a portal and its associated portlets.
It is this latter example that we will build here. The example is a simple shared library that you can plug into a WebLogic Portal application to collect analytics on the Portals in your application. Here is a screenshot of the application, click on the image to see it full-size.
NOTE - this is only an example, it is not BEA supported code and should not be deployed into production environments. In other words, don't blame me if you try to use this in production and find issues <g>.
To use the analytics package, we first need to create a class that implements the AnalyticEventHandler interface. This is a simple class that defines a handful of methods, here is an example implementation.
public class AnalyticsHandler implements AnalyticEventHandler {
public AnalyticsHandler() {
}
public void init() {
}
public void dispose() {
AnalyticsManager.clear();
}
public void log(AnalyticEvent event) {
AnalyticsManager.logEvent(event);
}
}
The handler has three methods. The first method, init, is called once to initialize the handler and enables the handler to acquire any resources it may need. The second method, dispose, is called to allow the handler to dispose of any resources it has acquired.
The final method is called whenever an analytic event occurs that the handler may wish to aggregate or collect. This method is called often and as a result when implementing the handler it is very important to ensure that this method is well optimized so has not to impact the performance of the Portal. In our example above we delegate the handling of the AnalyticEvent to another class, for the purposes of this explanation I'm not going to get into the details of aggregating the statistics but you can view the source code attached to this entry for details if you wish.
The AnalyticEvent class contains a variety of information including the times for various lifecycle phases of the portlet. The lifecycle phases are pretty clear, the only confusing aspect is that there does not appear to be a phase for handlepostback but in fact handlepostback corresponds to the loadstate lifecycle so it is present. While not covered in the javadoc, the times returned by the methods in this class are in nano-seconds.
To install our handler, we need to add a file called com.bea.netuix.servlets.controls.analytics.AnalyticEventHandler in the classpath in the path META-INF\services. This file contains a single entry which is the class of the handler, for example:
com.bea.ps.portal.analytics.AnalyticsHandler
Now that our handler is installed we are ready to start getting some analytics. I have attached a pre-built library, bea-ps-analytics-1.0.0.war, that you can use to try this out. After downloading the library, add it as a shared library to your application. To do this in a development environment, go to Windows|Preferences in Workshop and expand the WebLogic node. There should be a secondary node called J2EE Libraries, click on this and add the bea-ps-analytics-1.0.0.war file.
Next in your portal WAR project, expand the WebLogic Deployment Descriptor node and right click the J2EE Libraries node. From the popup menu select the Add option and add the bea-ps-analytics library to the application. One caveat here, this will change your weblogic.xml file so make sure not to check this file into your SCM otherwise you may end up with a dependency on this library in production which you do not want to do.
Now that the library has been added, start your server and go to the URL http://localhost:7001/{portalApp}/analytics/index.jsp where you replace the {portalApp} token with the context root of your portal application. You should now see the analytics screen, click the Enable button to turn on the analytics. Open a new browser to your portal and start clicking around, come back to the analytics screen and click the Refresh button. You should see the various metrics displayed for the time you clicked around.
That about covers it, I have attached the source code to this entry for readers that may be interested in how this works in more detail. Please do remember that this is unsupported code and is not intended for use in production environments.
Shared Library: bea-ps-analytics-1.0.0.war
Source Code: bea-ps-analytics-1.0.0.zip
Posted by Gerald Nunn at 1:35 PM | Categories: WebLogic | | | Permalink
