Closed jerseyrobot closed 8 years ago
@glassfishrobot Commented Reported by jervi
@glassfishrobot Commented harlan said: Is it possible to remove the SpringWebApplicationInitializer from jersey-spring, allowing the developer to initialize spring?
@glassfishrobot Commented jmisur said: Why on Earth should jersey initialize spring context with SpringWebApplicationInitializer? This class obstructs normal spring deployment with default and custom configuration and furthermore does not even work, on Tomcat 7 always throws applicationContext.xml not found!
@glassfishrobot Commented chadshowalter said: Here's a related example. I'd like to test Jersey 2.3.1 endpoints (using JerseyTest and InMemoryTestContainerFactory) which are @Autowired. For test, I would use an application context that contains only the beans I'm testing (as shown below). This sample illustrates what I would like to do (but can't):
public class SampleEndpointTest extends JerseyTest {
public SampleEndpointTest()
{
super(new InMemoryTestContainerFactory());
}
private ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
public AutowiredInjectResolver springResolver = new AutowiredInjectResolver(applicationContext);
public static class MySpringInjectable {
public int i = 0;
}
public static class AppConfig {
@Bean
public MySpringInjectable myInjectablePerRequest() {
return new MySpringInjectable();
}
}
@Path("/")
public static class Resource {
public MySpringInjectable mySpringInjectable;
@Autowired
public Resource(MySpringInjectable mySpringInjectable)
{
this.mySpringInjectable = mySpringInjectable;
}
@GET
@Path("/singleton")
public String getAndIncPerRequest() {
return Integer.valueOf(++mySpringInjectable.i).toString();
}
}
@Override
protected Application configure() {
ResourceConfig rc = new ResourceConfig();
rc.registerClasses(Resource.class);
rc.register(springResolver);
return rc;
}
@Test
public void test1()
{
final javax.ws.rs.client.WebTarget singleton = target().path("singleton");
assertEquals("1", singleton.request().get(String.class));
assertEquals("2", singleton.request().get(String.class));
assertEquals("3", singleton.request().get(String.class));
}
}
@glassfishrobot Commented chunyang said: Is there any plan to fix the issue in next release?
@glassfishrobot Commented amsmota said: This is really annoying and it should be a easy fix to do. However, just to note that I upgraded Spring from 3.2.4.RELEASE to 3.2.5.RELEASE and the "workaround" didn't work, for some reason the jersey-spring3 WebApplicationInitializer is always invoked first. I had to put a @Order in my Spring WebApplicationInitializer to make it work again...
@glassfishrobot Commented amsmota said: I'm also having the problem in SpringComponentProvider that jervi initially refered, and not in tests but when trying to instanciate a container with ContainerFactory.createContainer(type, rc);
ServletContext sc = locator.getService(ServletContext.class);
if(sc != null) { ----------------> it's always null, so it will throw the applicationContext.xml not found error
// servlet container
ctx = WebApplicationContextUtils.getWebApplicationContext(sc);
} else {
// non-servlet container
ApplicationHandler applicationHandler = locator.getService(ApplicationHandler.class);
String contextConfigLocation = (String)applicationHandler.getConfiguration().getProperty(PARAM_CONTEXT_CONFIG_LOCATION);
if(contextConfigLocation == null) {
contextConfigLocation = DEFAULT_CONTEXT_CONFIG_LOCATION;
}
ctx = new ClassPathXmlApplicationContext(contextConfigLocation, "jersey-spring-applicationContext.xml");
}
@glassfishrobot Commented mkochco said: I'm also unable to figure out how to construct a JerseyTest that uses only Spring Java config. Can we get this bumped to a Major issue? The docs/examples around the spring support are very confusing. The comments above seems to suggest that only XML-based configuration is supported yet the user guide suggests there are limitations with this approach. The helloworld-spring-webapp example does not include a test (e.g. JerseyTest). If the framework does properly support a pure annotation based approach perhaps there should be two examples helloworld-spring-xmlconfig and helloworld-spring-javaconfig?
@glassfishrobot Commented damnhandy said: +1000 on removing the SpringWebApplicationInitializer. I got bit with this as well the other day and took me a while to figure out why Spring was telling me that another context was already initialized as well as making the assumption that i'm using an XML configuration. The jersey-spring integration should include a WebApplication initializer at all. Spring doesn't surprise us like this and calls the need for a WebApplicationInitializer in the documentation:
The jersey-spring docs should take the same approach and remove the SpringWebApplicationInitializer.
@glassfishrobot Commented konrad.garus said: I think it makes some sense, but it should be possible to turn it on and off.
I imagine this as the choice between Spring owning Jersey or the other way round. Perhaps in some cases it makes sense to have no Spring configuration on your own, but let Jersey bring up and own the context for you. I'm not sure how common that is, but theoretically it makes some sense.
To Jersey team: I'm willing to contribute a patch for it. How do you think it should be resolved though?
1. Temporarily add a property to disable SpringWebApplicationInitializer, deprecate and plan to remove the property and the initializer later. 2. Permanently add a property to control whether the initializer should be used or not. 3. Just remove the initializer.
@glassfishrobot Commented schrepfler said: Does this mean that essentially we can't use JerseyTest along with Spring DI in a Servlet 3.x scenario? How is this not major?
@glassfishrobot Commented kknd22 said: I made change base on chadsh's example action_369743 and made it work. changes are: 1. get ride of spring AutowiredInjectResolver 2. force jersey bypass the spring context xml loading and but using annotation instead by doing:
rc.property("contextConfig", new AnnotationConfigApplicationContext(AppConfig.class));
3. added
rc.register(SpringLifecycleListener.class).register(RequestContextFilter.class);
I tested and ran and it worked.
-Chris L.
**SampleEndpointTest.java**
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spring.SpringLifecycleListener;
import org.glassfish.jersey.server.spring.scope.RequestContextFilter;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;
import static org.junit.Assert.assertEquals;
public class SampleEndpointTest extends JerseyTest {
//private ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
public SampleEndpointTest()
{
//super(new InMemoryTestContainerFactory());
}
public static class MySpringInjectable {
public int i = 0;
}
public static class AppConfig {
@Bean
public MySpringInjectable myInjectablePerRequest() {
return new MySpringInjectable();
}
}
@Path("/")
public static class Resource {
public MySpringInjectable mySpringInjectable;
@Autowired
public Resource(MySpringInjectable mySpringInjectable)
{
this.mySpringInjectable = mySpringInjectable;
}
@GET
@Path("/singleton")
public String getAndIncPerRequest() {
return Integer.valueOf(++mySpringInjectable.i).toString();
}
}
@Override
protected Application configure() {
ResourceConfig rc = new ResourceConfig();
rc.property("contextConfig", new AnnotationConfigApplicationContext(AppConfig.class));
rc.register(SpringLifecycleListener.class).register(RequestContextFilter.class);
rc.registerClasses(Resource.class);
return rc;
}
@Test
public void test1()
{
final javax.ws.rs.client.WebTarget singleton = target().path("singleton");
assertEquals("1", singleton.request().get(String.class));
assertEquals("2", singleton.request().get(String.class));
assertEquals("3", singleton.request().get(String.class));
}
}
@glassfishrobot Commented schrepfler said: If we have this we can all go on a vacation this year
@glassfishrobot Commented schrepfler said: @kknd22 But in the end, do you have one or two active spring contexts (one kicked off by the annotationconfig and the other kicked off by SpringWebApplicationInitializer)?
@glassfishrobot Commented bjornharvold said: +1 for me on this one. The solution listed above works at random. If I have my own WebApplicationInitializer and Jersey's SpringWebApplicationInitializer is on the classpath, it's not sure which context will get loaded first but they will both try to load. I had to add an @Order(1) on my own initializer to make sure my context gets loaded first. Otherwise I ran into issues.
Trying to fix it with an empty applicationContext.xml file will not work either. App will complain.
This issue was created a year ago. It should definitely not be marked as minor.
@glassfishrobot Commented nyte said: What is the reason that this has not been fixed for so long? It's been almost 2 years now...
@glassfishrobot Commented maxn said: I found a workaround that worked for my situation (I only want to use Spring for IoC). I couldn't find a way to do this in the Jersey ResourceConfig, and I ended up needing an extra WebApplicationInitializer.
The @Priority is there to make sure it runs before the jersey-spring3 SpringWebApplicationInitializer.
@Priority(value = 1)
public class MySpringWebInitializer implements WebApplicationInitializer
{
@Override
public void onStartup(ServletContext container)
{
//Tell jersey-spring3 the context is already initialized
container.setInitParameter("contextConfigLocation", "NOTNULL");
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(MySpringConfig.class);
container.addListener(new ContextLoaderListener(appContext));
}
}
@glassfishrobot Commented kkrauth said: If you are using spring boot with jersey and plan to deploy the resulting WAR on a JBoss container, this issue will probably randomly bite you during startup. If the org.glassfish.jersey.server.spring.SpringWebApplicationInitializer is detected on the classpath first and runs before org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration initializer, you will most likely end up with a duplicate context exception. In a spring boot app that already extends the SpringBootServletInitializer, all you need is this:
@Priority(value = 1)
public class MySpringWebInitializer implements WebApplicationInitializer
{
@Override
public void onStartup(ServletContext container)
{
//Tell jersey-spring3 the context is already initialized
container.setInitParameter("contextConfigLocation", "NOTNULL");
}
}
It took a while to figure this out, so I agree with above posters that this should not be marked as minor. It outright breaks spring boot apps with jersey when deployed on servlet containers.
@glassfishrobot Commented stu_s said: I tried the workarounds suggested above, but it looks like the priority annotation is not being respected (I also tried @Order, since it looks like spring-jersey pulls in pre-4.1 spring versions):
INFO: Spring WebApplicationInitializers detected on classpath: [org.glassfish.jersey.server.spring.SpringWebApplicationInitializer@49357096, com.openx.tq.adquality.cbir.gateway.SpringApplicationInitializer@5372ad66]
Dec 29, 2015 6:55:02 PM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring root WebApplicationContext
Dec 29, 2015 6:55:03 PM org.apache.catalina.core.StandardContext listenerStart
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [applicationContext.xml]; nested exception is java.io.FileNotFoundException: class path resource [applicationContext.xml] cannot be opened because it does not exist
I still need to try the workaround suggested by @kknd22 ... but it would be nice to know why the jersey folks don't feel this is an issue - why in the world would you force the use of an xml based configuration in a framework designed to support a spec (JAX-RS) where the whole point of the framework is to avoid xml configuration in favor of annotation based configuration?
The logic of the design choice (including the obvious rejection of the idea that this issue is a genuine bug, considering the years this issue has sat around) is bizarre to say the least.
It should be noted that I'd actually prefer to use as little spring as possible, and generally prefer solutions that hew closer to java standards, but this bug may force me away from jersey and back into springs web framework sigh.
@glassfishrobot Commented svavra said: I'm sorry guys, but we (the Jersey team) cannot dedicate resources to fix Spring related issues (including this one) at the moment. In case you feel strongly about the issue, please consider contributing a fix by submitting a Github pull request.
We look forward to see the community contributing to Spring Jersey related issues and features! Thanks! Stepan
@glassfishrobot Commented Was assigned to svavra
@glassfishrobot Commented This issue was imported from java.net JIRA JERSEY-2038
@glassfishrobot Commented Marked as won't fix on Monday, January 11th 2016, 5:24:09 am
@johno1985 Commented Just for awareness, I have observed the same issue in the jersey-spring4 module. The workaround in @glassfishrobot's first post works for me.
The new jersey-spring3 module expects an XML based spring configuration. I'm able to work around the issue when running the application by having the following WebApplicationInitializer:
The last line makes jersey-spring3 skip initializing a new spring context in SpringWebApplicationInitializer (GitHub).
When writing integration tests that extends from JerseyTest, however, this doesn't work. Using tt>@RunWith(SpringJUnit4ClassRunner.class)</tt and supplying my config class to tt>@ContextConfiguration</tt, jersey-spring3 still tries to initialize a ClassPathXmlApplicationContext in SpringComponentProvider (GitHub), and fails because the default applicationContext.xml file isn't found.
Example code:
The MyServiceJerseyConfig class:
Environment
Spring 3.2.3, Jersey 2.2-SNAPSHOT
Affected Versions
[2.2]