JBoss

Home/Tag: JBoss

Session and Memory Leak on Wildfly 10

I recently re-wrote 10MinuteMail, my secure temporary email service, updating it from Seam 2 and JBoss 4.2 to Deltaspike and Wildfly 10.  Unfortunately I noticed a memory leak in the JVM.  During beta testing, the JVM heap usage would slowly grow, over 1-2 weeks until it reached an OOM (out of memory condition).

Memory Leak in the Old Gen on Wildfly 10

I performed analysis on heap dumps taken at various stages of memory consumption and discovered that the issue was HttpSessionImpl objects being held onto by the com.sun.faces.application.WebappLifecycleListener’s activeSessions property.  Since 10MinuteMail does some “manual” session expiration and management, and the new application is AJAX heavy, I figured I was doing something wrong.  But thanks to some great support on the JBoss.org forums, I soon learned that there is a bug in the Undertow sub-system that ships with Wildfly 10.0.0.FINAL which keeps old sessions around, causing a memory leak – 

[UNDERTOW-657] HttpSession never removed from activeSessions – JBoss Issue Tracker

The fix is to replace the Undertow modules that come with Wildfly 10 with the latest stable release versions.  You can find more details here – https://developer.jboss.org/message/959286#959286

Now my application works as expected with no memory leak or abnormal numbers of session objects.

JBoss Performance Tuning and MasterTheBoss

I review technical book manuscripts for a few different publishers, and recently had the pleasure of working on an upcoming book called JBoss Performance Tuning by Francesco Marchioni.  It’s coming out in December 2010 and will be a must have addition to your bookshelf if you deploy applications on JBoss.

The book contains extensive performance/load test results giving you hard data to work with when deciding which changes to make in your environment.  Some of the results were very surprising and the book has a lot of valuable data.

The author, Francesco Marchioni, also runs a popular JBoss related blog called MasterTheBoss.com.  There are a ton of great articles and posts there, so check it out!

JBoss JMS Doesn’t Create Tables with XA Datasource

The JBoss Messaging service (at least on JBoss 4.3 EAP) defaults to using a local Hypersonic database. For production use you’ll want to switch away from Hypersonic to a real database, such as Oracle (in this example).

If you’re using XA datasources in general, it’s tempting to go ahead and create the new DefaultDS datasource definition as an XA datasource (like the example one jboss-eap-4.3/docs/examples/jca/oracle-xa-ds.xml ). However, I’ve just discovered that if you do that the JMS startup service won’t successfully create the tables it needs. The HILOSEQUENCES and TIMERS tables get created by the UUID key generator service, but the JMS table creation silently fails and then you get errors like this:

[code] 11:16:32,161 ERROR [ExceptionUtil] ServerPeer[0] startService
java.sql.SQLException: ORA-00942: table or view does not exist[/code]

Switch the DefaultDS definition to a non-XA version, and it will create all of the JBM_* tables successfully.

Make Google Ignore JSESSIONID

Search engines like Google will often index content with params like JSESSIONID and other session or conversation scope params. This causes two problems: first the links returned in the Google search results can have these parameters in them, resulting in “session not found” or other incompatible session state issues. Secondly it can cause a single page of content, to be indexed multiple times (with differing parameters) this diluting your page’s rank.

I’ve posted two solutions to this issue in the past: Using Apache to ReWrite URLs to remove JSESSIONID and a more advanced solution of using a Servlet Filter to avoid adding JSESSIONID for GoogleBot Requests.

Now there’s an even better way to handle this. Google has added an amazing new feature to their Webmaster Tools which allows you to specify how the GoogleBot indexer should handle various parameters. You can ignore certain parameters such as JSESSIONID, cid, and others, and also specifically not ignore other parameters such as productId, skuId, etc…

Log into your Google Webmaster Tools, and select the site you wish to work with. Under “Site Configuration” -> “Settings” there is a new section at the bottom called “Parameter handling”. Click on “adjust parameter settings” to expand the parameter handling configuration for your site. Sometimes Google will suggest various parameters it has discovered while crawling your site, and other times you just enter the parameters you want Google to ignore or pay attention to.

Google Webmaster Tools Parameter Handling Interface

This is a much more elegant solution to the JSESSIONID problem, and also allows you to easily handle other parameters your site may use for either session state or dynamic content generation correctly. The only downside is that this only impacts Google, whereas with the correct configuration my older two solutions can handle any Search Engine Bot. Maybe other search providers will or do provide a similar feature.

Setting Cache Headers from JBoss

Having control over the HTTP response headers allows you to set cache related headers in responses for various content you’d like cached on the browser (or an intermediary proxy). I created the ATG Cache Control DAS pipeline Servlet a year ago, but when you’re using JBoss you need another solution.

Since the DAF pipeline is only executed for JSPs the pipeline Servlet it doesn’t allow you to set headers for the static media items you’re more likely to want to cache. I created a Servlet Filter which allows you to set cache headers in JBoss based on URI patterns. It doesn’t allow the same fine grained control that the old pipeline Servlet does, but it should work for most situations.

Servlet Filters are very similar to ATG pipeline Servlets in that they are executed within the lifecycle of a request and can read the request and modify the response. The filter I created gets configured from the web.xml and sets the response headers relating to caching. You can configure different instances of the filter for each cache time you need, an hour, a day, or a week, and map different URL patterns to the appropriate instances.

I’ve added the filter into Foundation, the open source ATG e-commerce framework project, which is hosted at the Spark::red Development Community.

The Servlet filter code looks like this:

[java] package org.foundation.servlet.filter;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

/**
* The Class CacheHeaderFilter.
*
* @author Devon Hillard
*/
public class CacheHeaderFilter implements Filter {

/**
* The Constant MILLISECONDS_IN_SECOND.
*/
private static final int MILLISECONDS_IN_SECOND = 1000;

/** The Constant POST_CHECK_VALUE. */
private static final String POST_CHECK_VALUE = "post-check=";

/** The Constant PRE_CHECK_VALUE. */
private static final String PRE_CHECK_VALUE = "pre-check=";

/** The Constant MAX_AGE_VALUE. */
private static final String MAX_AGE_VALUE = "max-age=";

/** The Constant ZERO_STRING_VALUE. */
private static final String ZERO_STRING_VALUE = "0";

/** The Constant NO_STORE_VALUE. */
private static final String NO_STORE_VALUE = "no-store";

/** The Constant NO_CACHE_VALUE. */
private static final String NO_CACHE_VALUE = "no-cache";

/** The Constant PRAGMA_HEADER. */
private static final String PRAGMA_HEADER = "Pragma";

/** The Constant CACHE_CONTROL_HEADER. */
private static final String CACHE_CONTROL_HEADER = "Cache-Control";

/** The Constant EXPIRES_HEADER. */
private static final String EXPIRES_HEADER = "Expires";

/** The Constant LAST_MODIFIED_HEADER. */
private static final String LAST_MODIFIED_HEADER = "Last-Modified";

/** The Constant CACHE_TIME_PARAM_NAME. */
private static final String CACHE_TIME_PARAM_NAME = "CacheTime";

/** The Static HTTP_DATE_FORMAT object. */
private static final DateFormat HTTP_DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);

static {
HTTP_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
}

/** The reply headers. */
private String[][] mReplyHeaders = { {} };

/** The cache time in seconds. */
private Long mCacheTime = 0L;

/**
* Initializes the Servlet filter with the cache time and sets up the unchanging headers.
*
* @param pConfig the config
*
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
public void init(final FilterConfig pConfig) {
final ArrayList<String[]> newReplyHeaders = new ArrayList<String[]>();
this.mCacheTime = Long.parseLong(pConfig.getInitParameter(CACHE_TIME_PARAM_NAME));
if (this.mCacheTime > 0L) {
newReplyHeaders.add(new String[] { CACHE_CONTROL_HEADER, MAX_AGE_VALUE + this.mCacheTime.longValue() });
newReplyHeaders.add(new String[] { CACHE_CONTROL_HEADER, PRE_CHECK_VALUE + this.mCacheTime.longValue() });
newReplyHeaders.add(new String[] { CACHE_CONTROL_HEADER, POST_CHECK_VALUE + this.mCacheTime.longValue() });
} else {
newReplyHeaders.add(new String[] { PRAGMA_HEADER, NO_CACHE_VALUE });
newReplyHeaders.add(new String[] { EXPIRES_HEADER, ZERO_STRING_VALUE });
newReplyHeaders.add(new String[] { CACHE_CONTROL_HEADER, NO_CACHE_VALUE });
newReplyHeaders.add(new String[] { CACHE_CONTROL_HEADER, NO_STORE_VALUE });
}
this.mReplyHeaders = new String[newReplyHeaders.size()][2];
newReplyHeaders.toArray(this.mReplyHeaders);
}

/**
* Do filter.
*
* @param pRequest the request
* @param pResponse the response
* @param pChain the chain
*
* @throws IOException Signals that an I/O exception has occurred.
* @throws ServletException the servlet exception
*
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse,
* javax.servlet.FilterChain)
*/
public void doFilter(final ServletRequest pRequest, final ServletResponse pResponse, final FilterChain pChain)
throws IOException, ServletException {
// Apply the headers
final HttpServletResponse httpResponse = (HttpServletResponse) pResponse;
for (final String[] replyHeader : this.mReplyHeaders) {
final String name = replyHeader[0];
final String value = replyHeader[1];
httpResponse.addHeader(name, value);
}
if (this.mCacheTime > 0L) {
final long now = System.currentTimeMillis();
final DateFormat httpDateFormat = (DateFormat) HTTP_DATE_FORMAT.clone();
httpResponse.addHeader(LAST_MODIFIED_HEADER, httpDateFormat.format(new Date(now)));
httpResponse.addHeader(EXPIRES_HEADER, httpDateFormat.format(new Date(now
+ (this.mCacheTime.longValue() * MILLISECONDS_IN_SECOND))));
}
pChain.doFilter(pRequest, pResponse);
}

/**
* Destroy all humans!
*
* @see javax.servlet.Filter#destroy()
*/
public void destroy() {
}

}[/java]

And the web.xml configuration looks like this:

[xml] <filter>
<filter-name>CacheFilterOneWeek</filter-name>
<filter-class>org.foundation.servlet.filter.CacheHeaderFilter</filter-class>
<init-param>
<param-name>CacheTime</param-name>
<param-value>604800</param-value>
</init-param>
</filter>

<filter>
<filter-name>CacheFilterOneDay</filter-name>
<filter-class>org.foundation.servlet.filter.CacheHeaderFilter</filter-class>
<init-param>
<param-name>CacheTime</param-name>
<param-value>86400</param-value>
</init-param>
</filter>

<filter>
<filter-name>CacheFilterOneHour</filter-name>
<filter-class>org.foundation.servlet.filter.CacheHeaderFilter</filter-class>
<init-param>
<param-name>CacheTime</param-name>
<param-value>3600</param-value>
</init-param>
</filter>

…….

<filter-mapping>
<filter-name>CacheFilterOneDay</filter-name>
<url-pattern>*.js</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheFilterOneDay</filter-name>
<url-pattern>*.css</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheFilterOneWeek</filter-name>
<url-pattern>*.jpg</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheFilterOneWeek</filter-name>
<url-pattern>*.gif</url-pattern>
</filter-mapping>[/xml]