Analyzing class metadata with the Springframework
What a shame I haven’t published anything for a long time. To make up for this long pause, I’m going to discuss how Spring can support with analyzing class metadata. These days everybody is crazy about using annotations in their frameworks. Occasionally you need to know what classes/methods are annotated. A good example might be to build a list of class names for all JPA entities (those classes annotated with javax.persistence.Entity and the like).
A simple use-case showing how to detect classes annotated as components
This use-case is using a few Spring APIs and some home grown code that controls resource matching (ClassResourceResolver and AnnotationMetadataMatcher).
import java.io.IOException;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.util.ClassUtils;
public class ClassResourceResolverTest {
@Test
public void testSelect() throws IOException {
// given
ClassResourceResolver scanner = new ClassResourceResolver(
ClassUtils.convertClassNameToResourcePath(AComponent.class.getPackage().getName()));
final MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(
new PathMatchingResourcePatternResolver());
// when
String[] classNames = scanner.select(new AnnotationMetadataMatcher(metadataReaderFactory, false,
Component.class));
// then
Assert.assertArrayEquals(new String[] { AComponent.class.getName() }, classNames);
Assert.assertTrue(metadataReaderFactory.getMetadataReader(classNames[0]).getAnnotationMetadata()
.isAnnotated(Component.class.getName()));
}
}
@Component
class AComponent {
}
@Repository
class ARepository {
}
ClassResourceResolver is doing the resource matching all from a set of resource paths
public String[] select(ClassResourceMetadataMatcher matcher) throws IOException {
List<String> result = new ArrayList<String>();
for (String path : resourcePath) {
String resourcePattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + path + "/**/*.class";
Resource[] resources = this.resourcePatternResolver.getResources(resourcePattern);
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader reader = matcher.match(resource, resourcePatternResolver);
if (reader != null) {
result.add(reader.getClassMetadata().getClassName());
}
}
}
}
return result.toArray(new String[result.size()]);
}
A ClassResourceMetadataMatcher is looking at the annotations present on a class resource
@Override
public MetadataReader match(Resource resource, ResourceLoader resolver) throws IOException {
for (Class<? extends Annotation> annotationType : annotationTypes) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
if (new AnnotationTypeFilter(annotationType, considerMetaAnnotations).match(metadataReader,
metadataReaderFactory)) {
return metadataReader;
}
}
return null;
}
Under the hood Spring is using the ASM bytecode library for looking into the class files. Not that no class examined by the code above is actually loading any of the class objects so it is fairly lightweight and not filling up your perm gen space. Still you need to make up useful search paths so you don’t end up scanning through you full classpath.
The full code just is available as a gist
git clone git@gist.github.com:73f21e68d24032ad7672.git
ActiveMQ in embedded mode
I’m using Apache ActiveMQ for testing JMS based code. With their Springframework support it’s pretty simple to embed it into a test suite.
Today I encountered a strange problem. After I had broken my test and fixed the bug later I was not able to restart the embedded ActiveMQ container.
Obviously java.io.EOFException: Chunk stream does not exist at page: 0 sounds like some temporary file data was corrupt. JobSchedulerStore creates a directory activemq-data/localhost/scheduler for storing scheduler data. Clean/delete that and it should be startable again. If you don’t need that then you are better off with completely deactivating that feature.
Spring has its own Expression Language as of version 3.0
Recently I have switched from the log4j logging framework to the logback framework. logback seems to have evolved into something better than log4j. It has cool features like the ability of logging into context specific logging files almost out-of-the-box (Using a sifting appender).
However the application has to provide the context (usually as MDC variables) which is kind of boring task to do.
So, I have implemented a fairly generic servlet that is able to provide almost any servlet request information for an active HTTP request. This servlet accepts a set of SpEL expressions (SpEL will be available with Spring 3.0) which are parsed and evaluated.
Finally it produces the evaluation results as MDC variables through the org.slf4j.MDC api (So I guess that might be usable for log4j as well).
slf4jmdc at...SpringExpressionSlf4jMdcRequestFilter mdcKeyExpression contextPath=${request.contextPath};username=${request.userPrincipal?.name}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
StandardEvaluationContext context = new StandardEvaluationContext(new RootObject(request));
ExpressionParser parser = new SpelExpressionParser();
String[] mdc = StringUtils.delimitedListToStringArray(mdcKeyExpression, ";");
Properties expressions = StringUtils.splitArrayElementsIntoProperties(mdc, "=");
Enumeration> keyNames = expressions.propertyNames();
while (keyNames.hasMoreElements()) {
String key = keyNames.nextElement().toString();
try {
Expression exp = parser.parseExpression(expressions.getProperty(key), new TemplateParserContext());
Object value = exp.getValue(context);
if (value != null) {
MDC.put(key, value.toString());
}
} catch (ParseException e) {
LOGGER.error("Parsing expression", e);
}
}
try {
filterChain.doFilter(request, response);
} finally {
while (keyNames.hasMoreElements()) {
MDC.remove(keyNames.nextElement().toString());
}
}
}
This servlet can be used about anywhere MDC variables are needed!
Migrating Spring projects to Spring 3.0.0 bundle repositories
Since Spring has started its strong support for OSGi, all spring libraries have been turned into OSGi bundles internally. Subprojects (e.g. Webflow) eventually also started to publish their jars using the bundle naming scheme. With Spring 3.0 it looks we are at the final destination. As long as you stick to the more populare frameworks you get a pretty complete repository of Maven artifacts.
On one side it is good to see a unique naming for all of the spring + subproject artifacts. But for those of you starting to migrate existing projects this causes a few headaches. Everything is fine as long as you can get all of your favourite libraries from one of the SpringSpource bundle repositories
SpringSource Enterprise Bundle Repository - External Bundle Milestones http://repository.springsource.com/maven/bundles/milestone SpringSource Enterprise Bundle Repository - SpringSource Bundle Releases http://repository.springsource.com/maven/bundles/release SpringSource Enterprise Bundle Repository - External Bundle Releases http://repository.springsource.com/maven/bundles/external
Fortunately they also provide a really helpful tool for searching their repositories.
But sometimes you won’t find your favourite ones and need to fall back to non-bundle style artifacts from non-Springsource repositories.
And here is the trouble: SpringSource is renaming the Maven artifactId for all of the bundles stored in their repositories (e.g. com.springsource.org.apache.commons.logging is the bundle equivalent of org.apache.commons.logging).
So let’s say you depend on artifact A that comes from the bundle repository (that depends on logging) and also depend on artifact B that comes from the standard maven repository (and also dependes on logging).
This will end up with a 2 dependencies to the logging artifact and Maven will not be able to distinguish since they have a different artifactId.
To resolve that, start adding dependency exclusions and dependencies for first level transitive dependencies.
I’m showing an example for the hades project that actually has a bug in their dependency description.
org.synyx.hades org.synyx.hades 1.0 org.aspectj aspectjrt org.aspectj com.springsource.org.aspectj.runtime 1.6.1
ServiceLoader or "Sometimes you just don't need a full blown dependency injection container"
I like Springframework very much and use it in my day to day business as it eases development in many places. A few days ago I started using Hibernate Envers in order to implement a simple auditing solution for a JPA based application. I was faced with the (typical) requirement that the username of the user currently logged in was written into the audit entity. With Envers you would do that using a org.hibernate.envers.RevisionListener. This is instantiated by the Envers runtime. That listener required access to the FacesContext (it’s a JSF based application) to get the user principal from the HTTP request. Not a big deal in general! Still I wanted to keep that testable and usable in various environments, so an abstraction was needed and finally something would have to inject that into my listener.
As I said I’m a fan of Spring and this would be the perfect job and eventually one would propose “yeah, let’s do Spring AOP with AspectJ weaving”. As this application was already in production I didn’t want to add that at this time and was seeking for something more simple but still elegant.
Java as of 1.6 has the concept of SPI and ServiceLoader already built-in. I had never used it before. Here it seemed to perfectly match and it did.
import java.security.Principal;
public interface UserPrincipalProvider {
Principal getUserPrincipal();
}
import java.security.Principal;
public class TestUserPrincipalProvider implements UserPrincipalProvider {
public Principal getUserPrincipal() {
return new Principal() {
public String getName() {
return "TEST";
}
};
}
}
A file named META-INF/services/at.package.auditing.UserPrincipalProvider would be responsible for registering the provider implementation, it only contains one or multiple class names:
at.package.auditing.TestUserPrincipalProvider
For getting a runtime instance of such a provider I implemented a helper like
public static <T> T lookup(Class<T> clazz) {
Iterator<T> iterator = ServiceLoader.load(clazz).iterator();
return iterator.hasNext() ? iterator.next() : null;
}
It is called like ...lookup(UserPrincipalProvider.class).
Spring IDE AOP Markers
I have used the AspectJ style for providing services with transactional proxies. Today I was working on a demo application for a JavaEE with Spring workshop. Accidentially I had started off with compiler settings for Java6. As a result I didn’t get the nice AOP markers in the left gutter.
Using the Spring AOP Event Trace I figured that Spring IDE was not able to analyze the byte code, it reported invalid class version errors.
I switched back to Java5 compatible byte code and they are shown again.
Change log4j logging level at runtime using JMX/Spring
Monitoring application servers is a daunting task. Without the right instrumentation of your code it is even impossible. I try to use log4j as much as possible and in general I’m very generous regarding logging levels. But occasionally the console just gets flooded with logging output. Typically then I would modify the logging configuration files and restart the server which may be very time consuming and unnecessary.
In the past I had used JMX to switch logging levels at runtime for various projects. The first time now I have tried this with the Spring framework and it worked like a charm (did you expect anything else). I’m making the solution public in case I need this for future projects and to document the steps required!
First, a bean is required that uses the logging API (here log4j) to manage the logging level.
/*
* Copyright 2002-2008 Martin Ahrer.
*
* $Id$
*/
package at.martinahrer.blueprint.log4j;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
import org.springframework.jmx.export.annotation.ManagedResource;
/**
* @author Martin Ahrer
*
*/
@ManagedResource(objectName = "at.martinahrer.blueprint.log4j:name=Logging", description = "Logging", log = true, logFile = "jmx.log", currencyTimeLimit = 15, persistPolicy = "OnUpdate", persistPeriod = 200, persistLocation = "logging", persistName = "logging")
public class Logging {
@ManagedOperation(description = "Set the logging level for a category")
@ManagedOperationParameters( { @ManagedOperationParameter(name = "category", description = "Logger category"),
@ManagedOperationParameter(name = "level", description = "Logging level") })
public void setLoggerLevel(String category, String level) {
LogManager.getLogger(category).setLevel(Level.toLevel(level));
}
@ManagedOperation(description = "Get the logging level for a category")
@ManagedOperationParameters( { @ManagedOperationParameter(name = "category", description = "Logger category") })
public String getLoggerLevel(String category) {
return LogManager.getLogger(category).getLevel().toString();
}
}
It’s been annotated with Spring JMX annotations to provide JMX meta data. Next, we need to export the bean as a JMX Bean. This is supported very well by the Springframework’s MBeanExporter.
Finally to connect to the MBeanServer using jconsole the JVM (running the MBean) must be started with the system property -Dcom.sun.management.jmxremote set.
