atom feed15 messages in org.codehaus.enunciate.userRe: [enunciate-user] How does Enuncia...
FromSent OnAttachments
Matt RaibleAug 14, 2009 3:27 pm 
Ryan HeatonAug 14, 2009 3:58 pm 
Matt RaibleAug 16, 2009 11:36 am 
Ryan HeatonAug 17, 2009 8:50 am 
Matt RaibleAug 18, 2009 7:33 am 
Matt RaibleAug 18, 2009 12:20 pm.patch
Matt RaibleAug 18, 2009 2:32 pm 
Ryan HeatonAug 18, 2009 3:51 pm 
Matt RaibleAug 18, 2009 8:16 pm 
Matt RaibleAug 18, 2009 9:32 pm 
Ryan HeatonAug 19, 2009 10:35 am 
Matt RaibleAug 19, 2009 12:16 pm 
Ryan HeatonAug 19, 2009 12:50 pm 
Matt RaibleAug 19, 2009 2:56 pm 
Ryan HeatonAug 19, 2009 3:22 pm 
Subject:Re: [enunciate-user] How does Enunciate go about configuring Spring
From:Matt Raible (ma@raibledesigns.com)
Date:Aug 19, 2009 2:56:36 pm
List:org.codehaus.enunciate.user

On Wed, Aug 19, 2009 at 3:51 PM, Ryan Heaton <stoi@gmail.com> wrote:

Try annotating your resource class with @com.sun.jersey.spi.inject.Inject("conversationDesignServiceProxy").

My resource class is my ConversationDesignServiceImpl class that's wired up as an @Component. @Inject is not allowed on classes according to my compiler. ;-)

So just to make sure I have everything straight:

You've got two type definitions. We'll call them "ConversationDesignService" (the interface, annotated with @WebService) and "ConversationDesignServiceImpl" (the implementation, annotated with @Path and @WebService(endpointInterface="ConversationDesignService").

Yes, I removed @Path from the interface to try to eliminate confusion, but didn't help.

In your custom spring context, you define a bean named "ConversationDesignServiceProxy" which is a proxy bean with a some AOP and whatever else you've got applied. The interface that bean exposes (i.e. the "proxyInterfaces") is ConversationDesignService and the "target" is an autowired instance of ConversationDesignServiceImpl. You've got an alias to the the "ConversationDesignServiceProxy" bean named "ConversationDesignServiceEndpointBean".

Is that accurate?

Yep.

Assuming the above setup, here's how Jersey should behave:

1. See if ConversationDesignServiceImpl is annotated with @Autowired. If so, it will use Spring's BeanFactory.createBean(ConversationDesignServiceImpl.class, AUTOWIRED, false) method to instantiate the bean on every request.

When you say @Autowired, you mean Jersey's Autowired, not Spring's correct? I'll assume it's Jersey's and I'm not annotating my Services/Resource with any Jersey annotations.

2. If ConversationDesignServiceImpl is NOT annotated with @Autowired, it will look for an instance of ConversationDesignServiceImpl in the bean factory. If the class is annotated with @Inject, then it'll just use that bean name. Otherwise, it'll try looking for an instance of the class. Note it's looking for the impl, so it shoudn't get either your proxy bean nor Enunciate's service bean.

I believe this should read "it will look for an instance of ConversationDesignServiceImpl in the bean factory. If the class has @Inject annotations on its fields, then it'll inject them.

3. If ConversationDesignServiceImpl is NOT annotated with @Autowired AND it couldn't find an instance of ConversationDesignServiceImpl in the context, Jersey will just create a new instance of ConversationDesignServiceImpl (NOT autowired) and use it to handle the request.

This seems to be what's happening. Jersey's SpringComponentProviderFactory doesn't find any beans that are of the type ConversationDesignServiceImpl and if I debug it and explicitly set the bean name, the following error occurs:

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'conversationDesignServiceProxy' must be of type [com.company.app.service.ConversationDesignServiceImpl], but was actually of type [$Proxy86] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:168)

I guess the best way to solve this is to re-define my bean so it's not a proxy and can be found by Jersey.

Thanks for all the help.

Matt

On Wed, Aug 19, 2009 at 1:17 PM, Matt Raible <ma@raibledesigns.com>wrote:

It seems like this is the same problem I was experiencing with Enunciate finding my autowired class in Spring's ApplicationContext. To solve it, I created an alias:

<beans:alias name="conversationDesignServiceProxy" alias="ConversationDesignServiceEndpointBean"/>

This allowed the ServiceEndpointFactoryBean to find the correct object. It seems like you're saying I could do this same "bridge work" for Jersey by adding @com.sun.jersey.api.spring.Autowire to my implementation class. I did this and I get as far as the autowiring blowing up because it can't find one of my @Component objects that's in the ApplicationContext from <context:component-scan>.

When I was debugging last night, it looked like Jersey was finding two implementations of my bean: 1) the proxy which is wired and ready and 2) the enunciate ServiceEndpointFactoryBean. I tried excluding one at a time in hopes that one was initialized properly, but no dice.

You'd think there'd be a way to tell Jersey "I've already wired up this endpoint, here's the name". Kinda like the bean alias thing for Enunciate.

On Wed, Aug 19, 2009 at 1:36 PM, Ryan Heaton <stoi@gmail.com>wrote:

Jersey's lifecycle management is different (according to the JAX-RS spec). Jersey will only look in the Spring context for instances of your resource class (i.e. the implementation) and NOT for instances of the interface. I had to extend the com.sun.jersey.spi.spring.container.SpringComponentProviderFactory class to apply Enunciate's global intercepetors to the resource class.

If you're okay with Jersey managing the resource lifecycle (and you just need it autowired), try applying the @com.sun.jersey.api.spring.Autowire annotation to your resource class. If you need to manage the lifecycle of that class by yourself then you're going to need to figure out how to surface an instance of your resource class into your Spring context somehow.

On Tue, Aug 18, 2009 at 10:32 PM, Matt Raible <ma@raibledesigns.com>wrote:

On Tue, Aug 18, 2009 at 6:51 PM, Ryan Heaton <stoi@gmail.com>wrote:

Hi. Sorry for the mess with the ServiceEndpointFactoryBean; it's really just there for backwards-compatibility support for the "globalServiceInterceptors" element for spring config. Sometimes I wonder if it would just be easier to break with backwards compatibility and just get rid of it...

Anyway, I'm curious about your conclusion that it doesn't look in the parent context to resolve beans. The API documentation for the "beanNamesForTypeIncludingAncestors" method says:

"Get all bean names for the given type, including those defined in ancestor factories. Will return unique names in case of overridden bean definitions."

And it's pretty clear if you look at the implementation of the method that it does look for parent bean factories...

Yes, you are correct. No patch is needed and the parent is indeed inspected.

And about coaxing Enunciate into knowing that the bean already exists... the only way right now is by naming your beans (or providing aliases, whatever) to be the value of defaultImplementationBeanName, which right now is [SimpleName]EndpointBean. So if your interface were named "com.mycompany.MyService" then you'd name your spring bean "MyServiceEndpointBean".

I was able to do this with <beans:alias name="conversationDesignServiceProxy" alias="ConversationDesignServiceEndpointBean"/>. I was also able to write a servlet that gets both "conversationDesignServiceProxy" and "ConversationDesignServiceEndpointBean" and calls methods on them.

public class TestServicesServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); ConversationDesignService service = (ConversationDesignService) ctx.getBean("conversationDesignServiceProxy");

System.out.println("view definitions: " + service.getViewDefinitions().length);

ConversationDesignService service2 = (ConversationDesignService) ctx.getBean("ConversationDesignServiceEndpointBean");

System.out.println("view definitions2 ... "); ViewDefinition[] defs = service2.getViewDefinitions(); for (ViewDefinition def : defs) { System.out.println("def: " + def); } } }

However, when I call the same "getViewDefinitions" endpoint as a restful service (annotated method on interface below), I get an error:

@WebMethod @GET @Path("/viewdefinitions") ViewDefinition[] getViewDefinitions();

Error:

java.lang.IllegalArgumentException: [Assertion failed] - this argument is required; it must not be null at org.springframework.util.Assert.notNull(Assert.java:112) at org.springframework.util.Assert.notNull(Assert.java:123) at com.company.app.service.ConversationDesignServiceImpl.getViewDefinitions(ConversationDesignServiceImpl.java:160) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:198) at $Proxy86.getViewDefinitions(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:156) at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:67)

Here's the implementation of getViewDefinitions():

public ViewDefinition[] getViewDefinitions() { Assert.notNull(viewRepository); List<ViewDefinition> viewDefsList = viewRepository.loadViewDefs(); ViewDefinition[] result = new ViewDefinition[viewDefsList.size()]; viewDefsList.toArray(result); return result; }

From what I can tell, the service is autowired correctly, Enunciate is getting it correctly (figured this out by debugging ServiceEndpointFactoryBean), but it's somehow not working when Jersey invokes it. :-/

FWIW, I'm able to successfully call this method (ConversationDesignServiceImpl.getViewDefinitions) from the Enunciate-generated GWT Endpoint.

There should be a better way to do this. Perhaps we could have a custom annotation or a config option that says "let me manage the lifecycle and AOP of this service by myself"? Thoughts?

On Tue, Aug 18, 2009 at 1:20 PM, Matt Raible <ma@raibledesigns.com>wrote:

I think I've discovered the problem. First of all, Enunciate's ServiceEndpointFactoryBean does not look in parent context files to resolve beans. I believe the attached patch will solve this.

After I make this change, I receive Autowiring errors that I'm guessing are caused by the ServiceEndpointFactoryBean. Since my services are already registered in Spring XML files, is it possible to setup the service beans (in applicationContext.xml) so they use the "serviceImplementationBean" property instead of "defaultImplementationBeanName" and "defaultImplementationBeanClass"?

Thanks,

On Tue, Aug 18, 2009 at 10:34 AM, Matt Raible < ma@raibledesigns.com> wrote:

I no longer get an error on startup - thanks. However, my beans still have "Impl" in their name. Is that "as designed"?

Also my Spring objects are no longer getting wired up properly. I'm using @Autowired in my service implementations and their @Autowired fields are not getting set. My project does have a fair amount of custom Spring tags, so it's possible this problem is not Enunciate's fault.

On Mon, Aug 17, 2009 at 11:50 AM, Ryan Heaton <stoi@gmail.com

wrote:

No JIRA; I just fixed it out-of-band. I'm deploying 1.14 final today.

On Sun, Aug 16, 2009 at 12:36 PM, Matt Raible < ma@raibledesigns.com> wrote:

Is there a JIRA # for this so I can track when it's fixed?

Thanks,

On Fri, Aug 14, 2009 at 4:59 PM, Ryan Heaton < stoi@gmail.com> wrote:

Wow. You've somehow managed to trigger an old error condition that was once needed, but now it isn't anymore. Basically, the JAX-WS RI was limited in its ability to peer into objects to see their metadata, so if an endpoint interface bean was being proxied (or otherwise wasn't an instance of the endpoint implementation class) then JAX-WS RI would freak out. But since Enunciate is now creating a instrumented implementation class for the endpoints, we don't need that validation check anymore.

I'll get rid of it before I cut 1.14 final.

On Fri, Aug 14, 2009 at 4:27 PM, Matt Raible < ma@raibledesigns.com> wrote:

I'm getting the error below after generating some services with Enunciate. These service implementations have @Autowire annotations. The strange thing in the error message below is that it seems to be referring to the implementation as an interface, rather than the actual interface. Rather than this:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enunciate:service:ConversationDesignServiceImpl' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception is org.springframework.context.ApplicationContextException: Found service implementation bean is not an instance of com.company.app.internal.service.ConversationDesignServiceImpl

I would expect this:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enunciate:service:ConversationDesignService' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception is org.springframework.context.ApplicationContextException: Found service implementation bean is not an instance of com.company.app.service.ConversationDesignService

Any idea why my "impl" is being referred to rather than the interface? I'm guessing "no", but writing these questions seems to often help me figure out the solution. ;-)

Thanks,

SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enunciate:service:ConversationDesignServiceImpl' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception is org.springframework.context.ApplicationContextException: Found service implementation bean is not an instance of com.company.app.internal.service.ConversationDesignServiceImpl at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:480) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409) at java.security.AccessController.doPrivileged(Native Method) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:423) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:728) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:380) at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3843) at org.apache.catalina.core.StandardContext.start(StandardContext.java:4350) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045) at org.apache.catalina.core.StandardHost.start(StandardHost.java:719) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045) at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443) at org.apache.catalina.startup.Embedded.start(Embedded.java:825) at org.codehaus.mojo.tomcat.AbstractRunMojo.startContainer(AbstractRunMojo.java:385) at org.codehaus.mojo.tomcat.AbstractRunMojo.execute(AbstractRunMojo.java:144) at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:483) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:678) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeStandaloneGoal(DefaultLifecycleExecutor.java:553) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:523) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:371) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:332) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:181) at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:356) at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:137) at org.apache.maven.cli.MavenCli.main(MavenCli.java:362) at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:41) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315) at org.codehaus.classworlds.Launcher.launch(Launcher.java:255) at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430) at org.codehaus.classworlds.Launcher.main(Launcher.java:375) Caused by: org.springframework.context.ApplicationContextException: Found service implementation bean is not an instance of com.company.app.internal.service.ConversationDesignServiceImpl at org.codehaus.enunciate.modules.spring_app.ServiceEndpointFactoryBean.initApplicationContext(ServiceEndpointFactoryBean.java:141) at org.springframework.context.support.ApplicationObjectSupport.setApplicationContext(ApplicationObjectSupport.java:73) at org.springframework.context.support.ApplicationContextAwareProcessor.postProcessBeforeInitialization(ApplicationContextAwareProcessor.java:70) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:350) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1331) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)