|
|

[an error occurred while processing this directive]
|
 |
 |
 |
introduction |
 |
 |
 |
|
Performance testing early in the development cycle is one of the most beneficial things that can be done to ensure a successful application roll out. Portal applications are no exception. Caching minimizes the amount of work that is unnecessarily repeated by an application, so caching is an essential element in ensuring high levels of application performance. Because components are flexible and have the ability to alter their appearance for each user, it is imperative that caching be fully exploited in every component possible. This paper outlines common caching techniques that are used for components to improve performance.
|
 |
 |
how components work |
 |
 |
 |
Components have three main methods as follows:
- initialize(EbiSession session)
- getComponentData(EbiPortalContext context, Map params)
- processRequest(EbiPortalContext context, Map params)
Components are invoked when a portal page, JSP or JSP tag that contains a reference to the component is invoked. For each component found (e.g. <S3COMP> tags) the main portal servlet will first determine whether the component has been initialized. Components with a configuration setting of "lifetime = true" will be instantiated once for the life of the server, or again if the configuration of the component is altered via the PAC. If the configuration setting is "lifetime = false," the component will be initialized every time it is accessed.
After it is determined that the component has been properly initialized, the main servlet checks the URL to see if there are any "C=" parameters included as part of the query parameters. For each C= parameter it finds, the main servlet will invoke the "processRequest" method of the component named in the parameter.
Finally, for each of the component tags found on the page, the main servlet will call the getComponentData method to retrieve the actual component content to be included in the final HTML.
Now, let's take a closer look at these methods and the kinds of processing that are best placed in each of them.
|
 |
 |
the getComponentData method |
 |
 |
 |
The getComponentData method is the main method of any component. In this method, the actual content of the component is processed and returned to the system to be included in the final page rendering. The getComponentData method is called for every component on a page, every time that page is requested. For this reason, caching considerations are most valuable when using the getComponentData method. Caching can be done within this method several different ways as follows:
- Total Content Caching
Some components provide data that is common to all users. This type of data is easily cached and reused. Once the content has been created and is in its final form, it can simply be stored and served up to all users who access the component. Since the data is not unique to any one user, it is stored in the portal cache area and is not associated with any individual session.
EbiPortal portal = EboPortalFactory.getPortal();
EbiCacheHolder cache = portal.getPortalCacheHolder();
StringBuffer MyStringBuffer = cache.getObjectInCache("MyComponent_FinalHTML");
if (MyStringBuffer == null)
{
MyStringBuffer = this.doTheHTMLProcessing();
cache.putObjectInCache("MyComponent_FinalHTML",MyStringBuffer);
}
context.setComponentContent(MyStringBuffer);
context.getContentType(EbiComponentConstants.MIME_TYPE_HTML);
In this example, the cache is first checked to see if the content already exists. If it does, then the content is returned with no processing required, otherwise the content is created and then returned. It is important to use the component name in the key when putting the content into the cache so that the uniqueness of the key is guaranteed.
In this code, however, the content would only be reprocessed if the object were removed from the cache for some reason. In most cases, the data will need to be updated periodically to ensure that it is correct. This can be done by checking a flag in the data or by using another indicator, and then reprocessing the data as follows:
EbiPortal portal = EboPortalFactory.getPortal();
EbiCacheHolder cache = portal.getPortalCacheHolder();
boolean flagRefresh = this.checkFlag();
StringBuffer MyStringBuffer;
if (!flagRefresh){
MyStringBuffer = cache.getObjectInCache("MyComponent_FinalHTML");
if (MyStringBuffer == null){
MyStringBuffer = this.doTheHTMLProcessing();
cache.putObjectInCache("MyComponent_FinalHTML",MyStringBuffer);
}
}else{
MyStringBuffer = this.doTheHTMLProcessing();
cache.putObjectInCache("MyComponent_FinalHTML",MyStringBuffer);
}
context.setComponentContent(MyStringBuffer);
context.getContentType(EbiComponentConstants.MIME_TYPE_HTML);
This same principal can be used to refresh the data on a timed schedule. A time value is placed in the cache, then compared against the current time. If the time elapsed exceeds the refresh time, then the data will be refreshed. In this example, the data is refreshed every 20 minutes. (In SilverStream's ePortal, there are methods currently on the context object to "get/setLastModified." These methods do not work properly and are being removed in subsequent releases. The code example used below is the proper method for using "last modified" times to control data generation.)
EbiPortal portal = EboPortalFactory.getPortal();
EbiCacheHolder cache = portal.getPortalCacheHolder();
Date LastModified = (Date)cache.getObjectInCache("MyComponent_LastModified");
if (LastModified==null)
{
cache.putObjectInCache("MyComponent_LastModified",new Date());
MyStringBuffer = this.doTheHTMLProcessing();
}
else
{
Calendar myCalendar = Calendar.getInstance();
myCalendar.add(Calendar.MINUTE,-20);
Calendar cacheCalendar = Calendar.getInstance();
cacheCalendar.setTime(LastModified);
if(myCalendar.after(cacheCalendar))
{
//time to refresh
cache.putObjectInCache("Component1_LastModified",new Date());
MyStringBuffer = this.doTheHTMLProcessing();
}
else
{
MyStringBuffer = cache.getObjectInCache("MyComponent_LastModified");
if (MyStringBuffer == null) MyStringBuffer = this.doTheHTMLProcessing();
}
}
- Partial Content Caching
In many cases the content being created will be somewhat global and somewhat personalized. There are several ways to approach this situation. The content can either be broken up into separate components so that the global content is entirely separate, or a portion of the content can be cached inside a single component. The important thing is to try and use caching wherever possible. For example, if the content is part global to all users and part specific to an individual user, the global information can be cached in the portal cache, and the user-specific content tags can be cached with the session ID in the cache. To put objects in the cache associated with a specific session, access the cache off the "EbiSession" object.
GlobalContent = cache.getObjectInCache("MyComponent_Header");
EbiSession session = context.getEbiSession();
PersonalContent = session.getObjectInCache("MyComponent_MyData");
GlobalContent.append(PersonalContent.toString());
Remember to always check for null values after retrieving information from the cache as the portal will remove items using a "least recently used" algorithm if memory usage exceeds the set limits. This self-balancing mechanism is exactly why the cache is used to store data that can be reprocessed or re-retrieved. Large chunks of data, if attached directly to the session, have the possibility of using up all the server resources because that data cannot be garbage collected until the session has timed out. Using the cache ensures that the portal can get the memory it needs at times of high volume. Only small bits of information related to state management should be kept on the session itself, and this should be done by using the context.setValue().
- XML/XSL Component Considerations
When the content that is returned to the presentation manager from the component is XML, additional processing is necessary. If the XML is returned as a string, it must be parsed into a DOM (document) object before it can be processed using the XSL. One simple thing that can be done to improve performance of XML components is to build and return document objects rather than strings. Not only is the document object more efficient than string parsing to begin with, but it will save an extra processing step when the XML needs to be converted using the XSL.
Once the XML is in DOM format, the XSL is retrieved from the system and applied to the XML. XSL stylesheets are cached by the system as compiled objects, which eliminates any preprocessing on the XSL document after the initial load of the stylesheet. The two documents are then processed, and the resulting component HTML is added to the final HTML content that will be returned to the browser. It is important to realize that this XSL process can be extremely resource intensive and include data sorts and iterations through large quantities of data. Even if the two individual pieces are properly preprocessed and cached, the final processing can potentially create a big bottleneck. When XSL is suspected to be resource intensive, try testing individual components under load.
Another possibility is to try to reduce the number of times the XML/XSL transformation takes place. The portal itself will do the processing every time the page is accessed. But this can be a big resource drain, especially for data that does not change often. In this situation, consider moving the XML/XSL processing inside the component.
In this case, the appropriate style would be retrieved from the style manager, and the processing would be done within the component. The resulting HTML could then be cached and reprocessed/returned as desired by the system administrator.
EbiPortal portal = EboPortalFactory.getPortal();
EbiCacheHolder cache = portal.getPortalCacheHolder();
Document xmldata;
Document xslstyle;
String finalhtml;
//see if the final html has already been stored
finalhtml = (String) cache.getObjectInCache("HRComponent_html");
if (finalhtml==null)
{
//final html isn't in cache see if any other pieces are if not create and cache them
xmldata = (Document) cache.getObjectInCache("HRComponent_Data");
xslstyle = (Document) cache.getObjectInCache("HRComponent_style");
if (xmldata==null)
{
xmldata = this.getXMLdata();
//if you want to build a string of xml just convert it to a DOM
//xmldata = EboXmlHelper.getDOM(xml.toString());
if (xmldata!=null)
{
cache.putObjectInCache("HRComponent_Data",xmldata);
}
else{ //throw an error it didn't work }
}
if (xslstyle==null)
{
EbiSession session = context.getEbiSession();
//get the component manager
EbiComponentManager compmgr = (EbiComponentManager)
portal.getPortalManagerInterface(EbiPortalConstants.PORTAL_COMPONENT_MGR);
//get the style manager
EbiStyleManager stylemgr = (EbiStyleManager)
portal.getPortalManagerInterface(EbiPortalConstants.PORTAL_STYLE_MGR);
//get the component info object
EbiPortalComponentInfo compinfo = compmgr.getComponentInfo(session,"com.sssw.portal.component.hr.HRComponent",false;
//get the default style id
String defaultstyleid = compinfo.getComponentDefaultStyleID();
//get the default style
xslstyle = stylemgr.getStyle(session,"com.sssw.portal.component.hr.HRComponent",defaultstyleid,false);
if (xslstyle!=null)
{
cache.putObjectInCache("HRComponent_style",xslstyle);
}
else{ //throw an error it didn't work }
}
//process the 2 doms into the final html and store that in the cache
finalhtml = EboXmlHelper.processXML(xmldata,xslstyle);
cache.putObjectInCache("HRComponent_html",finalhtml);
}
//set the content for the component
context.setContentType( EbiComponentConstants.MIME_TYPE_HTML );
context.setComponentContent( finalhtml );
In the above example, not only is the resulting HTML cached, but also the XML and XSL DOM objects. This helps to avoid unnecessary processing. This technique can also be combined with the "last modified" code presented in the earlier example.
Think creatively about caching in components. For example, when working with a drop-down list box that is common to many components, store the HTML for that list in the cache. Since the list won't be for a specific component, there is no need to put a component class name in the cache key. If the object in cache happens to be null, the various components can call a common reprocessing method to recreate and store the list.
cache.putObjectInCache("vendor_list",vendorDDLB);
Finally, be sure and create standards for the cached object keys. This will help ensure that component writers won't store information under the same name, thereby overwriting objects.
Controlling the processing within the component can directly control application performance, and caching at all levels possible is critical to ensure efficient component processing.
|
 |
 |
conclusion |
 |
 |
 |
|
Caching for components is the most critical thing that can be done for improving component performance. The level of caching and types of caching will be unique to each component. Identifying data and objects that can be reused across components, users, and requests is the first step. Do not underestimate the benefit of caching and reusing even the smallest of data pieces. When component access is multiplied by hundreds-even thousands-of users and requests, seemingly small efforts can bring huge benefits.
|
 |
|
 |
 |
 |