Wednesday, October 14, 2009

Log4J: Emailing specific errors only

Log4J's SMTPAppender provides enough basic functionality to send out error messages as emails. Although emailing error messages is not always a good idea, unless something really goes wrong in system. Sometimes it is required to send out only specific errors in specific areas of the system. It is possible to do that with the SMTPAppender, even if these specific errors lie in the same category(WARN, ERROR etc.) as other errors. This is achieved with TriggeringEventEvaluator. Here is a sample config for the SMTPAppender.


<appender name="EMAIL" class="org.apache.log4j.net.SMTPAppender">
<param name="To" value="kamran.zafar@xeustechnologies.org" />
<param name="From" value="server-errors@xeustechnologies.org" />
<param name="Subject" value="SERIOUS ERROR" />
<param name="SMTPHost" value="mysmtphost" />
<param name="SMTPUsername" value="server-errors" />
<param name="SMTPPassword" value="password" />
<param name="Threshold" value="ERROR" />
<param name="BufferSize" value="1" />
<param name="EvaluatorClass" value="org.xeustechnologies.test.log4j.SmtpTrigger" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{dd MMM yyyy HH:mm:ss.SSS}][%p][%t][%c] - %m%n%n" />
</layout>
</appender>


The parameter EvaluatorClass is where we specify the implementation of TriggeringEventEvaluator, which acts like a filter to allow emailing only specific errors:


package org.xeustechnologies.test.log4j;

import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.TriggeringEventEvaluator;

public class SmtpTrigger implements TriggeringEventEvaluator {

public boolean isTriggeringEvent(LoggingEvent event) {
/*
* Email errors
*/
if( event.getLoggerName().equals( "SomeSeriousErrorLogger" )
&& event.getLevel().equals( Level.ERROR ) ) {
return true;
}

return false;
}
}


Now only ERRORs from "SomeSeriousErrorLogger" will be emailed.

Wednesday, August 19, 2009

Merging arrays in Java

I was playing around with java arrays and ran into a problem where I wanted to merge smaller arrays into a single big array. So the first thing I thought was to make a new array of size equal to the total length of all smaller arrays and then populating the values using loops. Then I went on to make a generic method to merge Generic arrays of same type but I realized that I cannot create a Generic array. So I thought of another way to merge Generic arrays using List.


public <T> T[] merge(T[]... arrays) {
List<T> list = new ArrayList<T>();

for( T[] array : arrays )
list.addAll( Arrays.asList( array ) );

return list.toArray( (T[]) Array.newInstance( arrays[0][0].getClass(), list.size() ) );
}


This code here will merge any number of generic arrays into one single array. The last line gives an unchecked cast warning, but can be ignored because we already assumed that the arrays are of same type.

Friday, May 08, 2009

Resolving log4j.dtd error on JBoss

I recently faced an issue where the application was not able to find the log4j.dtd, when deployed on jboss. We were using our own repository selector for logging and an isolated classloader. But could see a "FileNotFoundException" for log4j.dtd in the JBoss log when the application was deployed, although the dtd was part of the log4j jar contained within the application. The reason for that was the xml parser was not looking in the classpath resources for the dtd, but was just looking in a path relative to the application's root directory.

Now in order to load the dtd from the classpath I updated the repository selector and set an EntityResolver to the DocumentBuilder so that when the parser encounters the dtd reference the resolver looks it up from the classpath. Here is the code to that.

Assuming that the doctype reference in log4j.xml is:


<!DOCTYPE log4j:configuration SYSTEM "org/apache/log4j/xml/log4j.dtd">


The code to override the default behavior of the parser to look for the dtd in the classpath:


import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
.
.
.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver( new EntityResolver() {
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
// Check for dtd ref
if( systemId.endsWith( "org/apache/log4j/xml/log4j.dtd" ) ) {
// return the dtd from classpath
return new InputSource( getClass().getClassLoader().getResourceAsStream( "org/apache/log4j/xml/log4j.dtd" ) );
}

// Resume normal flow
return null;
}
} );

// now parse the log4j.xml
Document doc = builder.parse( "WEB-INF/classes/log4j.xml" );


Now our EntityResolver will return the dtd from classpath whenever the parser encounters it. Although I wish the parser could support the "classpath" protocol in the doctype uri.

Friday, April 24, 2009

Guice and JMX on JBoss

It is very easy to use Guice's runtime injector bindings to register MBeans on any instance of mbean server. Lets look at a simple example of a HelloMBean. In this example a simple MBean is created and registered on JBoss using google-guice runtime injection.


/**
* Hello MBean
*/
public interface HelloMBean{
public String sayHello(String name);
}


The implementation of this MBean needs to have a method for the injection of the MBeanServer.


/**
* HelloMBean implementation
*/
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import com.google.inject.Inject;

public class Hello implements HelloMBean{
public String sayHello(String name){
return "Hello " + name;
}

/**
* This method is used to inject an MBeanServer instance
* that will be used to register this MBean
*/
@Inject
public void register(MBeanServer server){
try {
server.registerMBean( this, new ObjectName( "Hello:type=Hello" ) );
} catch (JMException e) {
e.printStactTrace();
}
}
}


Now all that needs to be done is creating an Injector with bean and server bindings. The following piece of code can be used in any class (like a Servlet) to register the MBean when the application is deployed or as part of your injection bootstrap.


import javax.management.MBeanServer;
import org.jboss.mx.util.MBeanServerLocator;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.tools.jmx.Manager;
.
.
.
Manager.manage( "ExampleMBean", Guice.createInjector( new AbstractModule() {
@Override
protected void configure() {
// Bind the Jboss MBean server instance
bind( MBeanServer.class ).toInstance( MBeanServerLocator.locateJBoss() );
bind( HelloMBean.class ).to( Hello.class ).asEagerSingleton();
}
} ) );


You can also use "ManagementFactory.getPlatformMBeanServer()" to get/create an MBeanServer. Unfortunately this method cannot be used to get/locate the current JBoss MBeanServer, so "MBeanServerLocator" in the last code snippet above is the only way to locate a JBoss MBean Server instance. After the bean is registered you can use jboss jmx-console or jdk's jconsole to access the mbean.

Friday, April 03, 2009

RegEx based Spring AOP auto-proxy creator

When using Spring AOP in a medium-large application to add behavior to objects using crosscutting, it is desirable to have auto-proxy creators rather then creating a proxy for each bean (Spring AOP is proxy based). When auto-proxying a subset of beans, BeanNameAutoProxyCreator is most commonly used. But it has certain limitations, you have to provide a whole list of beanNames, which in a fairly large application becomes cumbersome and sometimes hard to maintain. Now because the application we were using had fairly well categorized beans with similar IDs, it was convenient to use a pattern or regular expression to identify them, but BeanNameAutoProxyCreator doesn't support regular expressions. So I extended the BeanNameAutoProxyCreator to provide regular expression support.



package xeus.spring.aop;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class RegExpBeanNameAutoProxyCreator extends BeanNameAutoProxyCreator implements ApplicationContextAware {

private ApplicationContext applicationContext;

@Autowired
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

public void setPattern(String pattern) {

String beans[] = applicationContext.getBeanDefinitionNames();
List<String> proxiedBeans = new ArrayList<String>();

Pattern pat = Pattern.compile( pattern, Pattern.CASE_INSENSITIVE );
for( String bean : beans ) {
Matcher matcher = pat.matcher( bean );
if( matcher.find() ) {
proxiedBeans.add( bean );
}
}

setBeanNames( proxiedBeans.toArray( new String [proxiedBeans.size()] ) );
}
}


This auto-proxy creator takes a regular expression and matches all the beans available in the Spring Application Context to create implicit runtime proxies for the matched beans. The bean definition for RegExpBeanNameAutoProxyCreator is similar to BeanNameAutoProxyCreator but it takes a "pattern" rather then the "beanNames".






^(?!.*(NoProxy)).*Impl$



someInterceptor





Here the "pattern" property takes the regular expression to match the beans available in spring context. It is pretty handy if you don't want to put the whole list of beanNames here; you can also use this to exclude beans as illustrated by the regular expression in this example.

Friday, March 27, 2009

Handling DWR Session timeouts

I recently had an issue in an application using Acegi and DWR, where even after the session invalidation the DWR calls could still be made. It seemed DWR calls were ignoring session timeouts. In order to over come this issue I wrote a little Ajax filter to check for session invalidation for DWR calls and throw an Exception if session has timed out. Throwing exception is more of a natural way of handling such scenarios then sending "plain/text" messages as recommended in DWR 2 API.


package myApp;

import java.lang.reflect.Method;
import org.directwebremoting.AjaxFilter;
import org.directwebremoting.AjaxFilterChain;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.extend.LoginRequiredException;

public class DwrSessionFilter implements AjaxFilter {
public Object doFilter(Object obj, Method method, Object[] params, AjaxFilterChain chain) throws Exception {

//Check if session has timedout/invalidated
if( WebContextFactory.get().getSession( false ) == null ) {
//Throw an exception
throw new LoginRequiredException( "This operation requires login." );
}

return chain.doFilter( obj, method, params );
}
}


Then attach this filter with DWR bean:










In version 3.0RC-2 or later it is possible to have a global filter (<dwr:global-filter>) that can be used by all DWR beans.

Now after this all you need to do is catch the thrown LoginRequiredException on the client side.


dwr.engine.setErrorHandler(errorHandler);

function errorHandler(message, exception){
//Session timedout/invalidated
if(exception && exception.javaClassName
== 'org.directwebremoting.extend.LoginRequiredException'){
//Reload or display an error etc.
document.location.reload();
}
}


Here reloading the page was helpful because we were using Acegi (now known as spring security) and we had the FilterSecurityInterceptor setup to take care of the request. DWR guys also recommend something similar but that requires reverse Ajax and it also is not a natural way of handling this.

In Acegi it is also possible to have method-level role-based authorization for DWR calls using the MethodSecurityInterceptor. I will try to explain that in the next blog entry.

Thursday, February 19, 2009

Disabled <select> binding problem

There is common problem while designing the frontends; sometimes the business logic requires the html fields to be readonly. Now the select html element doesn't have a "readonly" attribute, which forces the user to disable the select element. This causes another problem in frameworks like struts, spring-mvc etc where the fields are bound with the object model. The value of disabled fields will not be posted to the server and hence will not be bound with the model. There is a work around using javascript to simulate a readonly select box:


var selIndex;
function setupReadonly(selbox){
selIndex=selbox.selectedIndex;
}

function startReadonly(selbox){
selbox.options[selIndex].selected=true;
}

And in the plain html or spring-mvc etc. code do:






This will retain the initial value of the select box even if the user tries to change it.