Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

July 15, 2014

ArrayIndexOutOfException when creating a bean using Spring 3.0.5

I found a bug recently when using Spring 3.0.5, which appears to be fixed in 4.0.5. Here are the details.

If you have a class that has a parameterized constructor and also has a method that uses a lambda expression (introduced in Java 8), then a ArrayIndexOutOfException occurs when creating a bean for that class.

Here is the stack trace:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 17676
    at org.springframework.asm.ClassReader.readClass(Unknown Source)
    at org.springframework.asm.ClassReader.accept(Unknown Source)
    at org.springframework.asm.ClassReader.accept(Unknown Source)
    at org.springframework.core.LocalVariableTableParameterNameDiscoverer.inspectClass(LocalVariableTableParameterNameDiscoverer.java:114)
    at org.springframework.core.LocalVariableTableParameterNameDiscoverer.getParameterNames(LocalVariableTableParameterNameDiscoverer.java:86)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:193)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1003)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:907)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:580)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
    at LambdaSpringTest.main(LambdaSpringTest.java:9)


Here are the reproduction steps:

1. A sample class

import java.util.List;

public class LambdaSpring
{
    public LambdaSpring(){}
   
    public LambdaSpring(String arg1, String arg2){}
   
    public void method(List<Object> list)
    {
        list.stream().forEach(t -> System.out.println(t));
    }
}


2. Spring Bean configuration

<bean id="lambdaspring" class="LambdaSpring">
 <constructor-arg value="blabla"></constructor-arg>
 <constructor-arg value="blabla2"></constructor-arg>
</bean>


3. Running this main method will result in ArrayIndexOutOfException

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class LambdaSpringTest
{
    public static void main(String[] args)
    {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("lamdaspring.xml");
        LambdaSpring tmp = (LambdaSpring) context.getBean("lambdaspring");
    }
}


This issue appears to be fixed in Spring 4.0.5 (possibly earlier as well), but if you can't upgrade for some reasons, here are some workarounds:

I found that if you don't use the parameterized constructor, then you don't get this exception. Also, if you replace the lambda expression with the equivalent boilerplate code (i.e., implement Consumer interface), then you don't get this exception.


February 7, 2014

Useful logging mechanism: Mapped Diagnostic Context (MDC) in Logback

Quite often, enterprise-level web applications need to log sufficient information for troubleshooting problems but logging can be resource intensive and log analysis can be time consuming, especially if the application in question is deployed in production serving high volume of requests. Mapped Diagnostic Context (MDC)  in Logback is a useful logging mechanism that gives us the ability to select what to log. In this post, I describe a simple use case to demonstrate its usefulness.

If you are using Maven, you need the following dependencies in your pom.xml for this demo:

<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-api</artifactId>
 <version>1.7.5</version>
</dependency>
<dependency>
 <groupId>ch.qos.logback</groupId>
 <artifactId>logback-core</artifactId>
 <version>1.0.13</version>
</dependency>
<dependency>
 <groupId>ch.qos.logback</groupId>
 <artifactId>logback-classic</artifactId>
 <version>1.0.13</version>

</dependency>

In the following example, we would like "Starting app" and "Finishing app" statements to be logged. We would also like to log "Hello Parthy" but not "Hello Fred", both of which are DEBUG-level log statements. 

Using MDC:

    public static void main( String[] args )
  {
        Logger logger = LoggerFactory.getLogger("personal.tests.logback");
        
        logger.info("Starting app.");

        String username = "Parthy";
        MDC.put("username",  username);
        logger.debug("Hello {}", username);
        MDC.remove("username");

        username = "Fred";
        MDC.put("username",  username);
        logger.debug("Hello {}", username);
        MDC.remove("username");
        
        logger.info("Finishing app.");
  }

logback.xml (using MDCFilter that is shipped with logback):

<configuration>
 <turboFilter class="ch.qos.logback.classic.turbo.MDCFilter">
  <MDCKey>username</MDCKey>
  <Value>parthy</Value>
  <OnMatch>ACCEPT</OnMatch>
 </turboFilter>

 <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  <encoder>
   <pattern>%date [%thread] %-5level %logger - %msg%n</pattern>
  </encoder>
 </appender>

 <root level="INFO">
  <appender-ref ref="console" />
 </root>
</configuration>

Setting the OnMatch value in the turboFilter to DENY will prevent "Hello Parthy" getting logged. In this case, "Hello Fred" will also not be logged because the root logger level is INFO and "Hello Fred" is logged at DEBUG level.

To use MDC effectively, developers should put the customer support hat and ask how can I trace an individual request amidst tens or hundreds of thousands of request with minimal noise in the logs. Find out how requests are identified (request id, session id, user name etc) and use those as MDC keys. You might say that such filtering is CPU intensive. Actually, logback's turboFilter is intended for high performance.

Interested? Read more here: http://logback.qos.ch/manual/filters.html