Petter Mahlen
2012-01-03 10:04:27 UTC
Hi,
I've been trying to get a standard aspect that we're using in all our internal services to do performance monitoring to work in Jersey, but I've not been able to find a way to configure things to work. Maybe somebody here can help. Here's what I've tried:
First, the monitoring tool we have uses Spring AOP, an internal annotation (JamonMonitored), and a bean post processor to figure out which beans to create proxies for. There is an interface for the web resource:
public interface DataIngestionResource {
DataIngestionResponse ingest(DataIngestionRequest request);
}
The implementation is:
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Path("/data-ingestion")
public class JerseyDataIngestionResource implements DataIngestionResource {
private final DataIngester dataIngester;
public JerseyDataIngestionResource(DataIngester dataIngester) {
this.dataIngester = dataIngester;
}
@Override
@POST
@Path("/ingest")
@JamonMonitored(type = "type=DataIngestor,subtype=web")
public DataIngestionResponse ingest(@RequestParam DataIngestionRequest request) {
}
}
I'm also using the JamonMonitored annotation on other beans, notable the injected DataIngester implementation.
1. 'Regular', initial take:
In the spring configuration file, I specified:
<aop:aspectj-autoproxy />
This fails with the following error:
Jan 3, 2012 10:29:20 AM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Missing dependency for constructor public com.shopzilla.inventory.imp.di.webapp.JerseyDataIngestionResource(com.shopzilla.inventory.imp.di.DataIngester) at parameter index 0
The error message is pretty poor, as it makes you think that the problem is with the DataIngester that is being injected. However, removing JamonMonitored from the Jersey resource and leaving it in place for the DataIngester implementation works well. So the problem is in fact not that there is a dependency missing, but that Jersey is for some reason unable to instantiate the JerseyDataIngestionResource.
2. JAX-RS annotations on interface:
Based on a post I found (http://jersey.576304.n2.nabble.com/Jersey-and-AOP-td5963266.html), I tried to move the JAX-RS annotations to the interface instead. This fails with the following error message:
Jan 3, 2012 10:30:31 AM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Conflicting URI templates. The URI template /data-ingestion for root resource class $Proxy32 and the URI template /data-ingestion transform to the same regular expression /data-ingestion(/.*)?
So it seems there is some kind of conflict between the interface and the proxy? I'm not sure how to interpret this message.
3. Auto-proxy using CGLIB version:
The final thing I've tried is to use CGLIB for proxying by specifying
<aop:aspectj-autoproxy proxy-target-class="true"/>
This, too, fails, with the following error:
2012-01-03 10:34:07,399 [] ERROR [ContextLoader.java:227] Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataIngester' defined in class path resource [com/shopzilla/inventory/imp/di/wiring/DataIngestionConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.shopzilla.inventory.imp.di.FeedDataIngester]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
Presumably, the issue is that the class in question uses constructor-injected parameters (something I want to keep doing), and CGLIB-generated proxies require a no-arguments constructor, or something along those lines. There are some notes in the Spring documentation (http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/aop.html#aop-proxying) suggesting that constructors are called twice, something that won't work with final members.
Does anybody have any ideas of how this could be resolved?
Thanks,
Petter
I've been trying to get a standard aspect that we're using in all our internal services to do performance monitoring to work in Jersey, but I've not been able to find a way to configure things to work. Maybe somebody here can help. Here's what I've tried:
First, the monitoring tool we have uses Spring AOP, an internal annotation (JamonMonitored), and a bean post processor to figure out which beans to create proxies for. There is an interface for the web resource:
public interface DataIngestionResource {
DataIngestionResponse ingest(DataIngestionRequest request);
}
The implementation is:
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Path("/data-ingestion")
public class JerseyDataIngestionResource implements DataIngestionResource {
private final DataIngester dataIngester;
public JerseyDataIngestionResource(DataIngester dataIngester) {
this.dataIngester = dataIngester;
}
@Override
@POST
@Path("/ingest")
@JamonMonitored(type = "type=DataIngestor,subtype=web")
public DataIngestionResponse ingest(@RequestParam DataIngestionRequest request) {
}
}
I'm also using the JamonMonitored annotation on other beans, notable the injected DataIngester implementation.
1. 'Regular', initial take:
In the spring configuration file, I specified:
<aop:aspectj-autoproxy />
This fails with the following error:
Jan 3, 2012 10:29:20 AM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Missing dependency for constructor public com.shopzilla.inventory.imp.di.webapp.JerseyDataIngestionResource(com.shopzilla.inventory.imp.di.DataIngester) at parameter index 0
The error message is pretty poor, as it makes you think that the problem is with the DataIngester that is being injected. However, removing JamonMonitored from the Jersey resource and leaving it in place for the DataIngester implementation works well. So the problem is in fact not that there is a dependency missing, but that Jersey is for some reason unable to instantiate the JerseyDataIngestionResource.
2. JAX-RS annotations on interface:
Based on a post I found (http://jersey.576304.n2.nabble.com/Jersey-and-AOP-td5963266.html), I tried to move the JAX-RS annotations to the interface instead. This fails with the following error message:
Jan 3, 2012 10:30:31 AM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Conflicting URI templates. The URI template /data-ingestion for root resource class $Proxy32 and the URI template /data-ingestion transform to the same regular expression /data-ingestion(/.*)?
So it seems there is some kind of conflict between the interface and the proxy? I'm not sure how to interpret this message.
3. Auto-proxy using CGLIB version:
The final thing I've tried is to use CGLIB for proxying by specifying
<aop:aspectj-autoproxy proxy-target-class="true"/>
This, too, fails, with the following error:
2012-01-03 10:34:07,399 [] ERROR [ContextLoader.java:227] Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataIngester' defined in class path resource [com/shopzilla/inventory/imp/di/wiring/DataIngestionConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.shopzilla.inventory.imp.di.FeedDataIngester]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
Presumably, the issue is that the class in question uses constructor-injected parameters (something I want to keep doing), and CGLIB-generated proxies require a no-arguments constructor, or something along those lines. There are some notes in the Spring documentation (http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/aop.html#aop-proxying) suggesting that constructors are called twice, something that won't work with final members.
Does anybody have any ideas of how this could be resolved?
Thanks,
Petter