WebLogic
« Other | MainWSRP Portlets and Sessions
Wednesday, August 27, 2008
One question that comes up from time to time when using WSRP with WebLogic Portal is how are sessions handled with respect to portlets. When using portlets from the same producer, the question is are the portlets sharing the same session and if so is it a good practice for the portlets to share information using the session as a communication mechanism.
Let's deal with the first topic first, do portlets from the same producer share the same session. The answer is that it depends on how your consumer portal is configured. If you look at the file wsrp-producer-registry.xml in WEB-INF, you will see that each producer has a section called requires-initCookie. The values allowed in this XML setting include None, perUser and perGroup. A value of None is straight forward and means that the portlet does not use a session. The perUser value means that every portlet for every user gets its own session and thus no sessions are shared. A value of perGroup means that sessions are shared when portlets are from the same producer and in the same group. The group is determined by the portlet's groupId in the portlet definition (aka the .portlet file). Thus, portlets with the same groupId will share the same session for the same user if they are from the same producer.
As an aside, how cookies, and thus sessions, are handled from consumer to producer is actually part if the WSRP specification. If you check page 24 of the specification here, you will find the following under CookieProtocol Type:
This type is a restriction on the string type that is constrained to the values “none”, “perUser” or “perGroup”. These values carry the following semantics:
- ”none”: The Producer does not need the Consumer to ever invoke initCookie().
- ”perUser”: The Consumer MUST invoke initCookie() once per user of the Consumer, and associate any returned cookies with subsequent invocations on behalf of that user.
- ”perGroup”: The Consumer MUST invoke initCookie() once per unique groupID from the PortletDescriptions for the Portlets it is aggregating on a page for each user of the Consumer, and associate any returned Cookies with subsequent invocations on behalf of that user targeting Portlets with identical groupIDs.
Now for the second question, using the session as a communication mechanism. In general I believe this is a bad practice because it couples the portlets in a way that is fragile and not obvious to the consumer. Since the consumer controls how the sessions are managed, coupling portlets via the session means you are dependent on the consumer to use the right configuration. Another problem is when things are coupled such that Portlet B depends on a session value from Portlet A, what happens if the consumer elects to display B first?
Thus in general when communicating between portlets in WebLogic Portal containers prefer Inter-Portlet Communication as the preferred means for communicating between portlets. IPC decouples the portlets since the communication is event driven and not targeted at specific portlets.
Session sharing can be useful in certain cases, for example if both portlets are accessing the same information that is static for the lifespan of the session then this could be cached in the session. As an example, if we have two portlets that work with a customer object that is expensive to retrieve then there is benefit to sharing access to it in the session. However the portlets are not coupled because either portlet can retrieve the customer object directly if it is not in the session at the moment using a lazy initialization type pattern.
As with anything there is always exceptions. If for some reason your portlets need to pass a 500K piece of data back and forth it would be pretty inefficient to use IPC for this since the data would be sent over the wire up to the consumer and back down to the producer. But in general the exceptions are few and far between so stick with IPC for sharing information and do not use the session for this purpose.
Posted by Gerald Nunn at 12:25 PM | Categories: WebLogic | | | Permalink
More on InvalidSessionException
Thursday, July 31, 2008
In a previous post, WSRP Error Handling and InvalidSessionException, I talked about the need to trap and handle InvalidSessionException differently then other faults in a WSRP interceptor. This struck me as a bit odd because a developer should not have to worry about the type of fault thrown when writing an error handling interceptor. As a result I opened a support case with Oracle support for this issue in order to get clarification.
It turns out that the issue I was having with InvalidSessionException was due to a patch that we had deployed for another issue in WLP 10 GA. This patch, YTRS, was needed to fix an issue where InvalidSessionException would be generated if a portlet received a handleEvents or getRenderDependencies request first. Unfortunately it appears that it also broke the default InvalidSessionException handling when an interceptor is deployed.
This patch was fixed in 10.2 so this issue may be there as well, just keep this in mind when deploying an error handling interceptor.
Posted by Gerald Nunn at 9:11 AM | Categories: WebLogic | | | Permalink
WSRP Error Handling and InvalidSessionException
Friday, July 18, 2008
A common use case for interceptors in the WebLogic Portal framework is error handling. Using an interceptor allows a developer to trap error messages and display a more appropriate message to the end user while logging the error. The WebLogic Portal documentation contains an example of an error handler that can be found here http://e-docs.bea.com/wlp/docs100/federation/Chap-Interceptors.html#wp1031141.
There are two problems with that example though that developers should be aware of. First, the implementation shown is just an example and is thus somewhat naive. Specifically the example always returns HANDLED for faults and IO failures and thus never retries the request. In a production system under load where glitches do occur from time to time, eliminating all retries is not optimal and should be dealt with more intelligently.
The second issue is that there is one exception in particular that should be handled differently, this is com.bea.wsrp.faults.InvalidSessionException. This exception can occur when the producer session is missing some key information and typically is raised when the producer does not have the necessary templates from the consumer to format URLs. This can happen for a variety of reasons, such as fail over however the key point to understand is that Portal will automatically and seamlessly send a second request with the missing information. Thus the user never sees that an error occurred when this exception occurs.
So now that we have a grounding in InvalidSessionException, what does it have to do with error handling interceptors? The problem with this exception is that it is passed as a fault to interceptors just like any other fault. However, if the interceptor treats it as a normal fault and returns RETRY or HANDLED Portal will not send a new request with the missing information. In the case of HANDLED, no second request is sent while in the case of RETRY the original request is resent The only return value that causes Portal to deal with this fault correctly is CONTINUE_CHAIN.
Thus if you are writing an interceptor that handles faults it is highly recommend that you check the type of the Throwable being passed and if it is InvalidSessionException just return CONTINUE_CHAIN. Here is an example for an interceptor implementation of IBlockingInteractionInterceptor:
public OnFault onFault(IBlockingInteractionRequestContext requestCtx,
IBlockingInteractionResponseContext responseCtx, Throwable t) {
if (t instanceof InvalidSessionException) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("IvalidSessionException was generated and chain continued");
}
return OnFault.CONTINUE_CHAIN;
}
//.. rest of error handler here
}
I do have a support case open for this issue as this strikes me as a bit of a bug, developers should not have to code special handling for situations like this and RETRY should be considered semantically equivalent to CONTINUE_CHAIN for this situation in my opinion.
Posted by Gerald Nunn at 9:43 AM | Categories: WebLogic | | | Permalink
WebLogic Portal Statement of Direction
Thursday, July 10, 2008
In case readers missed it, a Statement of Direction was made available on the Oracle WebLogic Portal site after the July 1st middleware briefing by Thomas Kurian. All WebLogic Portal customers should review it closely, in particular the recommendations section at the end.
Posted by Gerald Nunn at 9:07 AM | Categories: WebLogic | | | Permalink
Building Site Maps for WebLogic Portal
Tuesday, July 08, 2008
I have added a new article that discusses two different options for building site maps in WebLogic Portal. With the addition of tree optimization back in 8.1 SP4, building a site map is no longer as trivial as it used to be and this is a topic I've been meaning to tackle for awhile. This article discusses a couple of different options for building the site map while keeping tree optimization on.
Click Building a Site Map in WebLogic Portal to read the article.
Posted by Gerald Nunn at 2:12 PM | Categories: WebLogic | | | Permalink
Customizing the Portal Admin Tool (PAT)
Monday, June 23, 2008
The Portal Administration Tool, aka the PAT, is a great tool for allowing administrators to manage a portal deployment. However sometimes you want to extend the console to add extra features that are missing or specific to your application to enable administrators to control everything from one place. Fortunately in WLP 10 this is now supported and here is a little example I put together showing how easy this can be.
The documentation for extending the PAT is quite easy to follow and is located at http://edocs.bea.com/wlp/docs100/extendadmin/extend.html. This documentation lays out the steps to setup your customized PAT skeleton quite well and I am not going to repeat them here. Instead I will talk about moving beyond the skeleton to create something a little more concrete.
If you have been reading my blog for awhile you have probably noticed I have been covering WSRP quite a lot in the last year or so. This is because I have been heavily involved in a large federated portal project at a financial institution here in Canada. Thus continuing with the WSRP theme, the example extension I built addresses a specific pain point with with respect to the operational aspects of a federated portal, namely how do aoperational staff disable a producer when it is down for maintenance.
Starting in WLP 10, the com.bea.wsrp.consumer.management.producer.ProducerManager class allows for producers to be pro grammatically disabled or enabled as needed. Unfortunately this API has not yet been exposed in the PAT meaning that if someone wishes to use it a custom interface to the API needs to be built. Rather then writing a standard servlet or JSP I thought it would make much more sense to write a little console extension to do it and integrate the API right into the PAT.
The first thing that needed to be done was to create a page flow portlet to manage this new functionality. I am not going to go into the code in huge amounts of detail as you can download the code and have a look for yourself, however we do need to cover some of the highlights. This extension is built as a portlet but you can also build extensions as pages and books as well. The location of an extension in the PAT is controlled by the netuix-extension.xml file and is covered in the PAT extension documentation I linked to earlier. Here is the netuix-extension.xml file this example is using.
<?xml version="1.0" encoding="UTF-8" ?> <weblogic-portal-extension xmlns="http://www.bea.com/servers/portal/weblogic-portal/8.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bea.com/servers/portal/weblogic-portal/8.0 netuix-extension-1_0_0.xsd"> <provider-info> <title>BEA PS - WSRP Production Operations Extension</title> <description>UNSUPPORTED EXTENSION - An extension that adds support for WSRP production operations, this is unsupported do not contact BEA Support for assistance.</description> <author>BEA PS Canada - Gerald Nunn</author> </provider-info> <portal-file>/portal.portal</portal-file> <page-extension> <page-location> <parent-label-location label="portalProducerSummaryPage"/> <page-insertion-point placeholder-position="99"/> </page-location> <portlet-content content-uri="/portlets/producerOperations/producerOperations.portlet" instance-label="ptlProducerOperations_1" title="Producer Operations"/> </page-extension> </weblogic-portal-extension>
The extension above tells the PAT to place this portlet on the producer summary page by specifying the parent-label-location label as portalProducerSummaryPage. Next the placeholder-position="99" in the page-insertion-point element is sufficiently large to have the PAT place the portlet at the end of all other portlets on this page. The content-uri attribute tells the PAT where to look for the portlet in our extension WAR.
In terms of the code itself, this portlet needs to integrate with the tree selection on the left side of the PAT since we need to know what portlet is active. To do this we use the com.bea.jsptools.patterns.tree.TreeStateBean class. Important. A big word of warning though, this class is not documented and supported by BEA so it may change in the future, use this with care and do not open support cases if it changes. I do have a support case open about this as the example here (http://edocs.bea.com/wlp/docs100/extendadmin/portletreuse.html) dealing with the reuse of PAT portlets also uses undocumented code from the same package. When I learn more I will post an update.
To use this class to get the current producer handle, I use the following code:
TreeStateBean bean = TreeStateBean.getTreeStateBean(getRequest(), false); String producerHandle = bean.getSelectedNodeLabel();
Once you get the handle it is straightforward to enable and disable the producer as required, feel free to browse the code to see the specifics. Here is a closeup image of the portlet, click on it to see a full screen view of the PAT with the portlet.

The example code to download is adminConsole.zip. To use the example, simply unzip it into a directory and then import it into an empty workspace using the Workshop option "Import existing projects". Once this is done you can create a Portal domain using the Configuration Wizard and then deploy these projects from Workshop into the new domain. One aspect I did not cover here that I will likely talk about in a future post is creating a customized admin console as a shared library to improve reusability and seperate the development of the extended console from the portal project.
One final word about the usefulness of this extension in a production environment. The ProducerManager APIs to enable and disable producers can be useful in certain situations however it does have a flaw in that it does not provide a message to the user that the portlet is not available. As a result users could be left scratching their heads wondering why certain portlets are not appearing in the Portal. To counter that you may want to integrate a message somewhere to notify them of what is happening. For example, the home page could have an alert portlet listing what producers are down or the message could be integrated into the portal's header or footer.
Posted by Gerald Nunn at 9:08 AM | Categories: WebLogic | | | Permalink
Renaming the appmanager in Portal
Tuesday, June 17, 2008
When you create a streaming desktop in portal it creates a URL that is formatted as follows:
http://server:port/context-root/appmanager/portal-name/desktop-name
This URL is configurable in the sense that developers and administrators can rename any part of the URL. The context-root portion is controlled from weblogic.xml while the portal-name and desktop-name portions are chosen by an administrator when the streaming desktop is created. The remaining portion of the URL, appmanager, can also be changed but it is not completely inituitive how to do it, hence this blog entry.
Changing this portion of URL is actually quite easy once you understand where it is defined. The appmanager portion of the URL is defined as a servlet in the Portal shared library BEA_HOME/wlserver_10.0/portal/lib/modules/wlp-framework-full-web-lib.war. Since this is a servlet, we can simply add a new servlet mapping to in order to make it accessible at a different location. For example:
<servlet-mapping>
<servlet-name>AppManagerServlet</servlet-name>
<url-pattern>/test/*</url-pattern>
</servlet-mapping>
Adding this mapping makes the portal available at the location http://server:port/context-root/test/portal-name/desktop-name. Of course this doesn't remove the appmanager mapping and that is still accessible, but adding a mapping does allow you to access the portal under a different name.
One caveat though is that while in theory adding a new mapping for a servlet in a shared library should work, I have found that occasionally I get errors when deploying a Portal application with this declaration. Specifically the deployment complains that no servlet with the name AppManagerServlet could be found. To work around this issue, I typically re-create both the servlet definition and mapping in my web.xml. Here is a complete example:
<servlet>
<servlet-name>MyAppManagerServlet</servlet-name>
<servlet-class>com.bea.netuix.servlets.manager.PortalServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MyAppManagerServlet</servlet-name>
<url-pattern>/test/*</url-pattern>
</servlet-mapping>
Posted by Gerald Nunn at 9:28 AM | Categories: WebLogic | | | Permalink
Filtering out unnecessary log messages
Monday, June 09, 2008
Sometimes you can have too much of a good thing and once in a while logging falls into that category. In a way logging is like beer, too little of it leaves you feeling unfulfilled whilst too much dulls the senses and makes accomplishing things difficult. Trying to find that middle ground can be difficult, but fortunately it is usually easy to tune the logging subsystem to display just the right level of information for the current situation.
Once in a while though you get into a situation where someone is unintentionally logging messages that are unnecessary and cannot be readily turned off by simply switching to a higher logging level. I recently ran into this situation at a client engagement where the WSRP subsystem in WebLogic Portal was logging a lot of unnecessary messages.
It turns out in this case that the extra logging is caused by a minor issue in Portal with page flows and WSRP. If you have a page flow action that takes a form bean as a parameter this form bean gets passed between the consumer and producer as part of an IPC event that encapsulates the action. The WSRP eventing system is designed to not attempt to deserialize event payloads unless explicitly requested, however due to an issue it does in this case. This leads to a ClassNotFoundException since the form bean class does not exist on the consumer and in turn causes the following message to be logged.
<[java.lang.ClassNotFoundException: portlets.main.TestBean ] Unable to deserialize event payload. See stack trace for details. java.lang.ClassNotFoundException: portlets.main.TestBean at java.lang.Class.forName(Ljava.lang.String;ZLjava.lang.ClassLoader;)Ljava.lang.Class;(Unknown Source) at java.io.ObjectInputStream.resolveClass(Ljava.io.ObjectStreamClass;)Ljava.lang.Class;(Unknown Source) at java.io.ObjectInputStream.readNonProxyDesc(Z)Ljava.io.ObjectStreamClass;(Unknown Source) at java.io.ObjectInputStream.readClassDesc(Z)Ljava.io.ObjectStreamClass;(Unknown Source) at java.io.ObjectInputStream.readOrdinaryObject(Z)Ljava.lang.Object;(Unknown Source) Truncated. see log file for complete stacktrace
This message was appearing quite often and made examining the logs more difficult then it needed to be, no longer could the administrators just grep for 'Exception' to find potential issues. The client wanted to eliminate the log message but did not want to raise the log level of the server since they wanted to be able to capture other messages at the Warning level.
Fortunately a workaround to the issue was relatively easy. WebLogic Server has a feature called Log Filters that is accessed through the WLS console that allows an administrator to determine what is or is not logged by evaluating a simple expression. In addition to an administrator creating a filter it is also possible to do this in code. For simplicity we chose to simply add an expression to the WLS console, however creating a custom class to do the filtering might be somewhat more performant.
To remove the message above we simply created a log filter with the following expression:
MSGID != 'BEA-423498'
Once the filter is created you just need to apply it to the various loggers in the console. To do this simply click on each server in the console and go to Logging tab for the server. Open the Advanced section to apply the filter which was just created to the appropriate loggers. This process is a bit of a pain to do manually, fortunately it is easy to script this using WLST.
Posted by Gerald Nunn at 3:35 PM | Categories: WebLogic | | | Permalink
Maven, WebLogic Portal and APPC
Tuesday, June 03, 2008
The WebLogic utility appc is an indispensable tool for pre-compiling JSPs and generating EJB classes. Unfortunately it turns out to be somewhat difficult to use appc with Maven and WebLogic Portal for a number of reasons.
The largest is that in WebLogic Server 10 the packaging of weblogic.jar changed significantly. Instead of being one uber-jar it is now a jar that is more focused on WebLogic implementation with many secondary features and interfaces moved to other jars in the modules directory. In order to make it easy to still compile applications by adding just weblogic.jar to a classpath, this jar now supports the JDK 1.5 extension mechanism whereby dependencies are linked in the manifest in a relative fashion.
The problem with this is that we cannot use Maven to pick up weblogic.jar from a repository since all the dependencies in the manifest are based on relative paths. One potential solution is to unwind the dependency tree and create equivalent dependencies in a repository by explicitly declaring dependencies in the libraries pom.xml. As you can imagine though this would be a fairly arduous task that would be somewhat brittle, every time a new release of WLS became available someone would have to once again unwind all the dependencies and create the necessary POMs.
I suspect that at some point Maven would support registering a file and the dependencies declared in a JAR automatically but that time has not yet arrived. Given I'm not a big fan of manual labor I opted to create a quick and dirty appc plugin that you can download from the products section here. This plugin is simply a wrapper around the appc utility that simply invokes it directly from a specified WebLogic home location. The plugin also comes with some convenience tools added to make using appc with Maven and Portal easier. For example, the plugin works around another appc limitation that allows only one shared library directory by allowing you to declare multiple shared library directories and concatenating them together before invoking appc.
Anyways more information is available on the products page so go check it out if you are interested. I also added a product page for my weblogic-library-plugin that was included in my original Maven and WebLogic Portal article. This version has been updated with some bug fixes since the article was written. Additionally it includes a new feature added by Kristel Nieuwenhuys that enables you to publish shared libraries to a remote repository.
Posted by Gerald Nunn at 8:58 PM | Categories: WebLogic | | | Permalink
Good introductory WLP and WSRP article
Monday, June 02, 2008
The developer.com site has a nice introductory article on WebLogic Portal and WSRP by Scott Nelson. If you are new to WebLogic Portal and WSRP it provides a great overview of the topic and is well worth reading. One nibble though is the author states the following on page 2:
Remote portlets are consumed in the producer with a proxy portlet, and WLP provides two ways to create a proxy portlet. One method is through the WebLogic Workshop IDE at design time. Defining a proxy portlet at design time requires providing an address to the producer in a .portlet file; this cannot be changed at runtime. Due to this drawback, this approach is not recommended in this article. If you want to see how it works in detail, you can follow the tutorial included in the WLP documentation.
This is not actually correct, the .portlet file saves a handle to the producer and not the producer WSDL URL containing the producer IP address. The IP address is stored in the producer WSDL in the WEB-INF/wsrp-producer-registry.xml file and this can be modified at runtime in the Portal Administration Tool (PAT). To do this, go to Portal Management in the PAT and open the 'Remote Producers' node under the 'Library' node. Click on the producer you want to modify and then scoll down to where the WSDL is shown in 'Producer Properties'. Click the edit button and modify the WSDL as needed.
Posted by Gerald Nunn at 2:44 PM | Categories: WebLogic | | | Permalink
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
Yet even more on the WLP ResourceProxyServlet
Friday, March 14, 2008
While I have blogged about the ResourceProxyServlet a couple of times, a little recap never hurts to set the stage. As many of you know, the ResourceProxyServlet is used in WSRP to proxy resources on the producer through the consumer to the browser. Essentially it provides a means for the browser to see resources on the producer even if the producer is behind a firewall by using the consumer a proxy.
Recently the client I was working with was having an issue where requests in a federated portal were taking a long time to complete and I thought it would be useful to talk about the experience to aid others who may encounter a similar problem. Using FireBug it was easy to see that the resource requests were timing out for some reason. Looking at the log we could see the following exception occurring a short time after the request:
java.net.ConnectException: Tried all: '1' addresses, but could not connect over HTTP to server: 'xxx.xxx.xxx.xxx', port: 'xxxxx'
at weblogic.net.http.HttpClient.openServer(HttpClient.java:361)
at weblogic.net.http.HttpClient.openServer(HttpClient.java:430)
at weblogic.net.http.HttpClient.<init>(HttpClient.java:159)
at weblogic.net.http.HttpClient.<init>(HttpClient.java:149)
at weblogic.net.http.HttpClient.New(HttpClient.java:265)
at weblogic.net.http.HttpURLConnection.connect(HttpURLConnection.java:170)
at com.bea.wsrp.util.CompatWsdlParser.getWsdlDocument(CompatWsdlParser.java:309)
at com.bea.wsrp.util.CompatWsdlParser.getWsrpPortUrls(CompatWsdlParser.java:64)
at com.bea.wsrp.wsdl.FixupWsdlParser.tryFixup(FixupWsdlParser.java:74)
at com.bea.wsrp.wsdl.FixupWsdlParser.parse(FixupWsdlParser.java:63)
at com.bea.wsrp.wsdl.WsdlInfoImpl.<init>(WsdlInfoImpl.java:169)
at com.bea.wsrp.wsdl.GlobalWsdlPool.getWsdlInfo(GlobalWsdlPool.java:55)
at com.bea.wsrp.consumer.resource.DefaultResourceConnectionFilter.getMarkupPortUrl(DefaultResourceConnectionFilter.java:79)
at com.bea.wsrp.consumer.resource.DefaultResourceConnectionFilter.allowedURL(DefaultResourceConnectionFilter.java:37)
at com.bea.wsrp.consumer.resource.ResourceProxyServlet.internalService(ResourceProxyServlet.java:219)
at com.bea.wsrp.consumer.resource.ResourceProxyServlet.doGet(ResourceProxyServlet.java:157)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:226)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:124)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:283)
at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
at com.bea.portal.tools.servlet.http.HttpContextFilter.doFilter(HttpContextFilter.java:60)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
at com.bea.p13n.servlets.PortalServletFilter.doFilter(PortalServletFilter.java:315)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3368)
at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
at weblogic.security.service.SecurityManager.runAs(Unknown Source)
at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2117)
at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2023)
at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1359)
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:200)
at weblogic.work.ExecuteThread.run(ExecuteThread.java:172)
Looking at the stack trace above, the issue appeared to be a connection hanging when the DefaultResourceConnectionFilter tried to get the WSDL from a producer. The obvious question was why was this class attempting to get a producer WSDL? Using JAD, a Java decompiler, along with Jadclipse it was easy to see what was happening by looking at the code.
In WLP 8.1, a connection filter was the primary way to secure the ResourceProxyServlet to ensure that it only accessed specific content. Otherwise the proxy servlet becomes a security hole since it could be used to access arbitrary resources from any machine the consumer had access to simply by manipulating the wsrp-url parameter in the resource request. You can view the original 8.1 documentation at Creating a Resource Connection Filter. In WLP 10 this feature is still available but somewhat deprecated in favor of the WebLogic Server connection filter.
Having said that, a default connection filter is still used with the resource proxy servlet and what it does is to ensure that the requested resource IP address matches an IP address for one of the producers markup address. This was where the WSDL request was coming from, the filter needs the markup address from the WSDL to do this comparison. The problem the client had was that the application's wsrp-producer-registry.xml had a dead producer in it, while none of the proxy portlets referenced it the default connection filter still used it to check resource requests. When it tried to get the WSDL for the dead producer the connection hung until a TCP timeout occurred since the IP referenced was not available on the test network.
So now that issue was out of the way, the client tried accessing the resource again and was finding that the resource request returned immediately but with a 401-Forbidden status code. Looking at the logs again we saw the following error being logged on the producer:
<Mar x, 2008 x:xx:xx PM EST> <Warning> <WSRP-Consumer> <BEA-420756> <[/xxxxxx] Resource http://xxx.xxx.xxx.xxx:xxxxx/xxxxx/javascript/xxxxxxxx.js requested by client from xxx.xxx.xxx.xxx not served.>
This is a message returned by the DefaultResourceConnectionFilter when it refuses to serve a request. Looking at the IP requested it matched the IP registered for the producer's WSDL so it was not immediately obvious what the issue was. However after looking at the code for the DefaultResourceConnectionFilter class, it showed that it was using the markup address and not the WSDL address for doing the comparison. When you access a producer WSDL you will see that it contains a variety of IP addresses in bindings for getting the markup, the service description and others which are created by the producer. These addresses are the ones actually used by the underlying WSRP implementation to interact with the remote portlet, not the WSDL address.
The customer had a clustered environment for the producer but did not have the front-end host parameters set, thus these bindings contained the IP and port of a server in the cluster rather then the appliance that was fronting the cluster. As a result the address for the resource, which was the front end appliance, did not match the server's address. The solution here was an easy one, setting the front-end host parameters for the producer cluster in the WebLogic console corrected the issue in that the generated WSDL service addresses now used the front end host settings. As a result the resource started being served correctly.
So there you have it, a few interesting tidbits learned about the ResourceProxyServlet.
Posted by Gerald Nunn at 4:01 PM | Categories: WebLogic | | | Permalink
Portlet Render Dependencies and CSS Image References
Friday, December 14, 2007
As some of you may know, WebLogic Portal enables a portlet to declare a dependency on CSS and JS files independent of the Portal, this feature is called render dependencies and is documented here http://edocs.bea.com/wlp/docs100/portlets/building.html#wp1073505.
This feature can be handy in a WSRP situation where a portlet may have a dependency on specific CSS that is unavailable to the consumer. In a WSRP scenario, the link to the CSS file is rewritten so that it is streamed to the browser from the producer using the consumer's ResourceProxyServlet. I won't spend time on ResourceProxyServlet as I've blogged about this previously at http://www.gexperts.com/blog/archives/03-01-2006_03-31-2006.html#9 so see that article for more info on this feature if you need it.
One of the issues with this feature though is that CSS is often dependent on images, in fact in this Web 2.0 world it is pretty rare to find a CSS file that doesn't contain image references. The problem we encounter with this in WSRP is that while the referenced images in the CSS exist on the producer the resolving of the image reference is done by the browser. For example, assume the following portlet CSS:
.test {
background-image: url('/portlets/test/css/blue.jpg')
}
When this CSS is passed to the browser, the browser will in turn connect to the consumer and attempt to download blue.jpg relative to the location of the CSS. However because the CSS was served by the consumer's ResourceProxyServlet, this reference will not work because the image is not located on the consumer and the browser will fail to resolve the image.
One option would be to hard code the reference for the consumer's ResourceProxyServlet in the producer's portlet CSS but this would be a poor solution indeed since we would be tightly coupling the producer CSS to the consumer. I was researching this further and when I looked over the render dependency documentation referenced above, there is a section called Rewriting Resource URLs that describes rewriting resource references so that they can be successfully streamed from the producer using the consumer's ResourceProxyServlet.
Thinking that this looked interesting I resolved to test it out. I created a small portlet and a CSS file that appears as follows as per the documentation.
.test {
background-image: url('wlp_rewrite?/portlets/test/css/blue.jpg/wlp_rewrite')
}
Next I created a render dependency file as follows and linked it to the portlet. Here is the render dependency file called test.dependencies:
<?xml version="1.0" encoding="UTF-8"?>
<p:window xmlns:p="http://www.bea.com/servers/portal/framework/laf/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.bea.com/servers/portal/framework/laf/1.0.0 laf-window-1_0_0.xsd ">
<p:render-dependencies>
<p:html>
<p:links>
<p:search-path>
<p:path-element>css</p:path-element>
</p:search-path>
<p:link rel="stylesheet" type="text/css" href="test.css" />
</p:links>
</p:html>
</p:render-dependencies>
</p:window>
I then linked this file to the portlet in question and gave it a try in a test application. There is a great tool called FireBug for FireFox that enables you to easily inspect CSS. Using this tool I was quite disappointed to see that the image reference in the CSS was not being rewritten.
At this point I was a bit stumped and made an inquiry on our internal Portal support mailing list. After some interesting discussions George Murnock, one of our Portal engineers, gave me the low down on what I was doing wrong. This feature only works if the CSS is inlined so that the Portal rewriters are used when writing the content to the response. To inline the CSS instead of referencing the content I needed to make some small tweaks to my dependency file as follows:
<?xml version="1.0" encoding="UTF-8"?>
<p:window xmlns:p="http://www.bea.com/servers/portal/framework/laf/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.bea.com/servers/portal/framework/laf/1.0.0 laf-window-1_0_0.xsd ">
<p:render-dependencies>
<p:html>
<p:styles>
<p:search-path>
<p:path-element>css</p:path-element>
</p:search-path>
<p:style type="text/css" content-uri="test.css" />
</p:styles>
</p:html>
</p:render-dependencies>
</p:window>
This causes the CSS file to be inlined with the HTML and thus Portal's rewriter will modify the URLs as needed. With this change the CSS above then becomes the following:
.test {
background-image: url('http://localhost:7001/consumer/resource?_windowLabel=ptlEventTest_2_1&wsrp-urlType=resource&wsrp-url=http%3A%2F%2Flocalhost%3A7001%2Fproducer%2Fportlets%2Ftest%2Fcss%2Fblue.jpg&wsrp-requiresRewrite=true&');
}
One minor annoyance with this technique is that it requires the CSS to be inlined in the HTML rather then delivered as a separate CSS file. The issue with this is that the CSS will not be cached by the browser. This somewhat mitigated by the fact that the content is rewritten only once at the WebLogic layer and then cached, however for large CSS files George suggested splitting the elements that need rewriting into separate and smaller CSS files to mitigate this further.
Thanks again to George Murnock for helping me out with this.
Posted by Gerald Nunn at 4:01 PM | Categories: WebLogic | | | Permalink
When in Rome...
Wednesday, August 08, 2007
The old expression "When in Rome do as the Romans do" applies to development as well. One thing I always try to encourage developers to do when building an application is to try to make their development environment as similar as possible to the production environment. Now obviously this isn't practical in all cases, if you are deploying to Solaris you probably don't want to foist Solaris on all your developers. Similarly it's not practical from a performance point of view to develop against a local clustered environment.
Having said that, when building a portlets that are intended to be used in a federated environment it is a good practice for developers to run and test these portlets as remote rather then local portlets in their development environments. When building a portlet, the tendency is to simply drop the local portlet onto a test portal in order to test the portlet's functionality. The problem with this approach is that there may be issues that only appear when the portlet is deployed in a federated environment, for example the developer relying on the same request object to be used throughout the portlet's lifecycle. These kind of issues can be caught and corrected early by simply testing the portlet as a remote portlet from the get go.
There is not much excuse not to do this as testing remote portlets is quite simple and convenient, instead of just dropping the local portlet on your test portal, create a new remote portlet whose WSDL points to the local instance of WLP that is running. Essentially everything is still running on the same WLP instance, however the portlet is running as a remote portlet allowing you to ensure that everything will work as expected in a federated environment.
Posted by Gerald Nunn at 4:01 PM | Categories: WebLogic | | | Permalink
More on ResourceProxyServlet in WSRP
Monday, June 25, 2007
An earlier blog post I wrote, WSRP and Remote Resources, talked about how to use the ResourceProxyServlet to serve resources such as images and css from the producer to the browser by using the consumer as a proxy. This is often necessary because the producer is behind a firewall and the browser cannot access resources on the producer directly.
One feature of the ResourceProxyServlet is that it also proxies response headers from the producer to the end user's browser. This is a nice feature because resources often have headers associated with them that control caching and expiration. Unfortunately, the set-cookie header can also be copied to the browser and if the consumer and producer share the same cookie name and path this can result in the producer cookie overwriting the consumer cookie causing a loss of session on the consumer.
This issue is well documented in the Federated Portals Guide under the Configuring Session Cookies section. The documentation available here presents two potential solutions to the problem, one involving changing the cookie name or path and the other blocking the proxying of all response headers.
I was recently assisting a client on WLP 10 who was experiencing this issue and neither of the above two options were particularly optimal for their situation. The client had existing infrastructure that relied on the JSESSIONID cookie name that would need to be re-configured if this was changed. The second solution would prevent the client from controlling some headers that they may want to tune in the future.
Fortunately, a new feature has been added in WLP 10 that provides a better solution then either of the two currently documented. In the file wsrp-producer-registry.xml, a new stanza has been added called resource-cookies. This allows developers to configure whether or not producers can set cookies when resources are requested through the ResourceProxyServlet. By default, this is set to block-none which allows the producer to set cookies as needed, by changing this to block-all the set-cookie header is no longer proxied from the producer thereby preventing the session issue in a much cleaner way then the current alternatives.
This feature should be documented on edocs in the near future. I would like to thank Subbu Allamaraju for his detailed explanation of this feature when I stumbled across it.
Posted by Gerald Nunn at 4:01 PM | Categories: WebLogic | | | Permalink
Maven 2 and Portal: Article Addendum
Thursday, May 31, 2007
Here is another follow-up blog entry on integrating Maven 2 and WebLogic Portal as that article seems to have generated quite a bit of interest. In particular there were a couple of questions and comments that came up a few times that I would like to address.
APP-INF/lib and Maven
The first question was how to deal with APP-INF/lib when using Maven, as most of you know APP-INF/lib provides a place to store library JARS that are accessible to the entire enterprise application. Traditionally, developers would drop the libraries into this directory and then simply manage them as an SCM artifact. If the library needed to be updated, the old version would be deleted from the SCM and a new version would be uploaded.
However this does not really conform with Maven's best practices where you would normally declare your dependencies in pom.xml and have Maven deal with it rather then do it manually. Fortunately there is an easy and convenient way to do this Maven and it provides a couple of significant benefits as follows:
- You don't need to check these artifacts into the SCM, instead Maven will resolve the dependencies and updating to a later version of a library is simply a matter of updating pom.xml.
- Maven resolves all the dependencies for the libraries declared, this means that if a library you want to use in turn depends on another library, this secondary dependency will automatically be included. So in this saves times since you don't have to figure out the dependency chain for a library.
In order to set this up for your own project, follow these instructions.
Step 1 - Add the libraries as modules to the EAR pom.xml
The first step is to add your libraries as jar modules to the EAR's pom.xml, this can be done by simply adding new elements to the module section as follows:
<modules>
...
<jarModule>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</jarModule>
...
</modules>
Note that anything you add here must also be added to the dependencies section of the POM along with the version number of the dependency. As mentioned earlier, if the declared library has dependencies on other libraries these will also be brought into APP-INF/lib. This is a significant benefit however one drawback of this is that sometimes you get a library you don't need because it is provided by the container. For example, commons-logging has a dependency on the javax servlet-api library but this is provided by WebLogic and is not needed in APP-INF/lib. To avoid dragging this JAR in, declare the dependency as follows:
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
Step 2 - Make APP-INF/lib the output directory for bundled libraries
The next step is to make APP-INF/lib the output folder for libraries when Maven builds the EAR rather then the root folder of the EAR. You can specify this in one of ways, either with each library or once by declaring APP-INF/lib as the default. I prefer the latter approach and this can be done by using the defaultLibBundleDir element in pom.xml as follows:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<configuration>
...
<defaultLibBundleDir>APP-INF/lib</defaultLibBundleDir>
...
</configuration>
</plugin>
Step 3 - Optionally enable copying dependencies to APP-INF/lib
Depending on how your development environment is configured, you may need to provide a mechanism for developers to physically copy the libraries to APP-INF/lib so that they can perform their development tasks. For example, if you are not using the Maven Eclipse plugin, these libraries need to be physically on the classpath in order for Workshop to pick them up.
To accomplish this goal, we will use the Maven 2 dependency plugin. It provides a goal called copy-dependencies that will accomplish what we need with a bit of configuration. To do this, first add a plugin to the build section of the EAR's pom.xml as follows:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<excludeScope>provided</excludeScope>
<includeTypes>jar</includeTypes>
<excludeTypes>war</excludeTypes>
<overWrite>true</overWrite>
<outputDirectory>/EarContent/APP-INF/lib</outputDirectory>
</configuration>
</plugin>
With this added a developer can then type mvn dependency:copy-dependencies to have all of the EAR dependencies copied into APP-INF/lib. We can also optionally add some functionality to the clean goal so that these libraries are removed from APP-INF/lib when mvn clean is invoked. To do this, we again add a plugin to the build section of the EAR's pom.xml as follows:
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<configuration>
<filesets>
<fileset>
<directory>${basedir}/EarContent/APP-INF/lib</directory>
<includes>
<include>**/*.jar</include>
</includes>
<followSymlinks>false</followSymlinks>
</fileset>
</filesets>
</configuration>
</plugin>
Finally, one potential issue is that most EAR projects that use Maven are organized with a parent project with an EAR, WAR and other projects organized as children of that parent. If the developer accidentally types mvn dependency:copy-dependencies at the parent project level a lot of dependencies in other projects, such as the WAR, will be copied to default locations needlessly. To prevent this, we can add an element to the parent project's pom.xml that stops this goal from doing anything by default. I find adding the following plugin declaration to the parent's pom.xml build section works well for me:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<excludeTypes>jar,war,ear,pom</excludeTypes>
<overWrite>false</overWrite>
</configuration>
</plugin>
Maven and Workshop Integration
Another question that arose frequently was how to integrate Maven with Workshop. When using Maven with Eclipse, there are two approaches that can be used as follows:
- Use the Maven Eclipse Plugin that manages pom.xml and creates a Maven classpath container with all of the dependencies in pom.xml.
-
Use the
mvn eclipse:eclipsecommand to generate an Eclipse project from a pom.xml
I have only tried the first approach, the second approach may work but I have not tested it. I was concerned that the second approach may not be feasible with shared libraries, something we wanted to leverage but admittely I haven't spent a lot of time looking into this. With regards to the first approach, the Maven Eclipse plugin has worked relatively well for me concerning it is still early days however I have only been able to get the 0.0.9 version to work, the current 0.0.10 version doesn't seem to play nicely with Workshop.
Maven WebLogic Shared Library Plugin
As part of the article I wrote, I developed a Maven plugin that would import a shared library into the Maven repository. There were a couple of bug reports from that including that it would not handle a SNAPSHOT version or group names that were nested with the dot notation. Both of these issues have been corrected and I have attached the latest source to this article, it can be retrieved here maven-weblogic-library-plugin.zip.
I also made a minor change in the way the plugin works, there are now two separate goals, one that installs a single library and one that installs a directory of libraries. If you use this updated version make sure to update your scripts accordingly.
Finally, if you are building your own shared libraries, I would recommend using the mvn install command to install them instead of this plugin. This plugin is designed to handle third party libraries and generates custom POMs for the libraries, by using mvn install your libraries are installed with the POMs you developed.
Maven APT Plugin
The Maven APT Plugin has been corrected to address the initial issues I had with it, I would highly recommend using the public version versus the patched version I included with the article.
Posted by Gerald Nunn at 4:01 PM | Categories: WebLogic | | | Permalink
Handling Global Parameters and Events in WLP 9.2
Monday, February 19, 2007
When building a portal, there is often a need to handle certain specific request parameters and inter-portlet events at a global level rather then at a portlet level, this blog post describes one technique for accomplishing this.
Inter-portlet communication (IPC) is a powerful technique for exchanging messages between portlets in a loosely coupled fashion, it allows portlets to work together with no knowledge of where each other is located in the Portal structure. However like most things, except for maybe Chuck Norris, it does have one achille's heel and that is the fact that only portlets can send and receive IPC events.
This issue becomes evident on larger portal projects when you may find quite a few instances where you would like to be able to send or receive an event on a global basis. As an example, when certain parameters are posted to the portal from an external web site you may want to send an event to initiate an action. Or maybe you want to have an event handled at a global level since it affects the portal itself rather then an individual portlet.
A common technique for doing this is to simply place a dummy portlet on a non-visible page. This works well with respect to sending and receiving events, but will not work if you want to send an event in response to posted parameters unless you force the hidden page to be active. Additionally, another weakness with this technique is that with streaming portals it is possible for an administrator to accidentally remove the portlet without understanding what it is doing.
An alternative technique that I prefer is to create a controller portlet that is embedded in the shell. Embedding the portlet in the shell ensures that the portlet will participate in every request and always be present. Additionally, the administrator can only remove the portlet by changing the shell which is not likely to happen and can be avoided by embedding the portlet in all available shells.
Embedding the portlet in the shell is straightforward, simply open the target .shell file and then use the netuix:portletInstance element to embed the portlet within the netuix:header element. Here is an example:
<netuix:header>
<netuix:portletInstance
instanceLabel="_eventController"
markupType="Portlet"
contentUri="/portlets/controller/controller.portlet"/>
</netuix:header>
Another thing I have been exploring is to combine this technique with Spring to register backing file services for use with the controller portlet. Instead of having one large backing file that handles everything, I have many small service classes that are injected into the backing file of the controller portlet. I’ll post more about this later.
Posted by Gerald Nunn at 4:02 PM | Categories: WebLogic | | | Permalink
Using XStream to Troubleshoot Sessions
Monday, February 12, 2007
XStream is a great library that serializes and deserializes objects to and from XML, here we use it to troubleshoot a problem with HTTP sessions.
Finding and detecting problems with session state is one of the more difficult troubleshooting tasks. It tends to be difficult because objects in the session often have complex graphs with many nested objects. Factor in replication and things can get nasty quite quickly.
Last year I was helping a client troubleshoot a session problem where it appeared that certain attributes in the session were not being persisted after changes. The client was coming from a Websphere background where the session state can optionally be serialized after every request automatically. They had already corrected the obvious deficiency and ensured that setAttribute was called after each change to an object in the session.
Since I was unfamiliar with the application I needed a way to dump the session in a way that was human readable. While reflection is one option it turned that the work had been done for me already. XStream is an open source toolkit that enables any object graph to be serialized and deserialized to and from XML. With XStream viewing the session in a human readable format, XML, is child's play and makes it easy to see exactly what is in the session.
Thus the problem became immediately obvious when using XStream to dump the session. What we found was that the same object instance was being stored in the session using multiple attributes. On a single machine this was not an issue since a change to the object through any attribute affected all attributes as the object was a single instance. Replication throws a spanner in the works though.
As you probably know, WebLogic replicates only the attributes that have changed which means that WebLogic serializes and deserializes each object individually unlike Websphere which has an inefficient option to serialize and deserialize the entire session after each request.
This means that under Websphere the object would maintain its singleton nature since the whole session graph is serialized and deserialized in one go. Under WebLogic when the objects against each attribute are serialized and deserialized individually they become separate object instances. Changes to an object against one attribute are no longer reflected when the object is accessed through a different attribute.
What the client was experiencing was that occasionally a request would blip causing a failover to the secondary server. Once on the secondary server instead of dealing with a single object instance in the session they were now dealing with multiple copies of the same object all being in a different state. Updates to one object would not be reflected in others thus making them think that the session was not being persisted.
In short, XStream was invaluable for detecting this problem, being able to view the session in a human readable format saved me a ton of time and I’d recommend it to others dealing with issues that require being able to follow a complex object graph.
Posted by Gerald Nunn at 4:02 PM | Categories: WebLogic | | | Permalink
WSRP and Remote Resources
Wednesday, March 01, 2006
If your new to Web Services for Remote Portlets (WSRP), you may not be aware of a feature in WebLogic Portal that enables users to access images and resources from a remote portlet.
Web Services for Remote Portlets (WSRP) is something I've recently been doing a lot of work with at various clients as this specification seems to be gaining some traction in the market. It is of particular interest to larger enterprise's that want to leave control of the development and deployment of functionality, including the user interface, to their various line's of business while still aggregating the functionality into a single application.
If your not familiar with WSRP, it's paradigm is that a consumer proxies access to content from various producers to the user's browser. For example, if a consumer accesses the portal, the portal (called the consumer) in turn accesses the various remote producers providing content on behalf of the user making the request. The HTML markup returned by the producers is then rendered inline with the rest of the content to the user's browser.
This means that the user never directly accesses any of the producers and often does not have physical access to the producers which can be behind a corporate firewall. As a result, a common question is how to make resources such as images available to the user. Keep in mind that resources are typically rendered as links or URLs in the content and the browser resolves these independently after it receives the HTML. If the resource is on a producer that is not accessible by the browser, the browser will fail to retrieve it.
Fortunately a relatively little known feature with respect to WSRP in WebLogic Portal is the ability to proxy images and other resources from the producer through the consumer to the user's browser. If you have a portal application and look at the web.xml for the application, you will notice that a ResourceProxyServlet is specified. This servlet is responsible for proxying resource requests from the consumer portal to the producer.
The way it works is that URLs for resources on the producers are mapped to the ResourceProxyServlet on the consumer. This mapped URL includes various parameters that enable the ResourceProxyServlet to determine where to go to retrieve the content. Thus when the user’s browser attempts to resolve this resource, it makes the request against the consumer, not the producer, and the consumer proxies the request to the underlying producer. In this way the user's browser never directly accesses the producer.
To use the proxying capability, we simply need to use the render:resourceUrl custom tag provided by WebLogic Portal. When this tag is used, WebLogic Portal automatically rewrites the URL to use the consumer resource proxy when the portlet is being accessed through WSRP. For example:
<img src='<render:resourceUrl path="/demo/resources/images/weblogic-img-lt.jpg"/>'>
Now keep in mind that this proxying obviously adds another hop to the process and can have some impact on performance. It likely is not as significant as one would first think since the browser caches most of these resources, however it is worthwhile to consider centrally locating common images and resources that are shared among multiple producers.
Posted by Gerald Nunn at 4:02 PM | Categories: WebLogic | | | Permalink
