Spring Data REST with Spring MVC: Adding RepositoryRestMvcConfiguration to existing DispatcherServlet - spring

I have an existing Spring MVC Application with a DispatcherServlet and an XML based configuration.
Now I would like to integrate Spring Data REST but I dont know how to do this in a clean way. I added
<context:component-scan>...</context:component-scan>
so my RestControllers are found but I fail in adding a RepositoryRestMvcConfiguration config. I tried the annotation driven approach which doesnt work
#Configuration
public class RestConfiguration extends RepositoryRestMvcConfiguration {
...
}
and the
<bean class="com.mypackage.rest.RestConfiguration" />
approach is not working either.
I also tried the follwing in the web.xml
<servlet>
<servlet-name>myservlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.mypackage.rest.RestConfiguration</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Strange thing is, a method annotated with #PostConstruct is called, but non of the configure* methods.
In the docs for Spring Data REST is a chapter where it is explained how to add a Spring Data REST to a Spring MVC application in code. It also says
The equivalent of the above in a standard web.xml will also work identically to this configuration if you are still in a servlet 2.5 environment.
How do you do this?

Fortunately, in Section 11.2 it is explained. Would have been nice to have a reference in Section 2.5 that points to Section 11.2 :-/
In Java, this would look like:
import org.springframework.context.annotation.Import;
import org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration;
#Configuration
#Import(RepositoryRestMvConfiguration.class)
public class MyApplicationConfiguration {
…
}
In XML this would look like:
<bean class="org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration"/>

Related

Convert Spring MVC project to JAX-RS

I'm trying to do a project with JAX-RS, Spring, Hibernate, Maven and JPA. I'm following this article as reference. This project is in Spring MVC. I'm very new to Spring MVC. I have tried to convert it to JAX-RS. What are the important changes I have to do with this?
The main change I noticed that in Spring MVC we use like this: #RequestMapping("/accounts/{username}").
But in JAX-RS we use like this. #Path("/accounts/{username}")
Therefore If I do the changes like this, what are the other key changes we have to do with this?
The most basic thing you need to do in order to port the Spring MVC controllers to JAX-RS controllers, is to provide and integration between Spring and your JAX-RS provider. One way to do that is to make the appropriate changes to web.xml.
Assuming you are using Jersey 2.x and Spring 3.x, the web.xml would look like:
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<module-name>helloworld-spring</module-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<servlet>
<servlet-name>SpringApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>some.package.MyApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringApplication</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Then MyApplication would look like:
package some.package;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spring.scope.RequestContextFilter;
public class MyApplication extends ResourceConfig {
public MyApplication () {
register(RequestContextFilter.class);
register(JerseyResource.class);
register(SpringSingletonResource.class);
register(SpringRequestResource.class);
register(CustomExceptionMapper.class);
}
}
You will also need the dependency
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>2.10.1</version>
</dependency>
besides the other Spring 3 and Jersey 2 dependencies.
Once you get that out of the way, you can start transforming the controllers.
For example:
#Path("admin")
#Component
public class AdminController {
#Autowired
private SchoolService service;
#Path("load-home-page")
#GET
public String loadHomePage(Map model){
return "admin/index";
}
//the rest of the code accordingly
}
The documentation for Spring-Jersey integration is here while a complete example can be found here.
Personally I would go the Spring MVC route instead of Jersey if you really don't have a compelling reason to choose Jersey over Spring MVC.

EnableLoadTimeWeaving did not weave other config in web application context

I try to use spring weblogic LTW in my project to do some aop stuff. my project is a simple webapp servlet2.5 use spring mvc 3.2.6, running on weblogic 10.0.
I have following app level configuration setup in web.xml
#Configuration
#EnableLoadTimeWeaving
public class AppConfig {
}
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = { "com.blabla.model" })
public class CoreConfig {
}
I also have a mvc level configuration setup in my web.xml
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.blabla.controller" })
public class MVCConfig extends WebMvcConfigurerAdapter {
}
here is my simplified web.xml
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>AppConfig,CoreConfig
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>MVCConfig
</param-value>
</init-param>
<init-param>
<param-name>wl-dispatch-policy</param-name>
<param-value>RestWorkManager</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
so what happens is, MVCConfig and its scanned components are all woven by LTW and works great. but CoreConfig and its scanned components (all the DAO) are not picked up by LTW.
I guess that the CoreConfig and AppConfig is in the same level, so when AppConfig and CoreConfig are loaded, the LTW is not triggered yet.
And I tried to put the CoreConfig in the same level as MVCConfig, it got picked up by LTW.
but CoreConfig is supposed to be application level, not dispatchservlet level. Since many spring web MVC applications use a root context and a child for the DispatcherServlet.
so My question is if I put CoreConfig in the app level, how to make LTW pick it up? Thanks.
Loadtimeweaving will only work for classes that aren't already loaded by the class loader.
Now when using XML configuration the actual bean classes are loaded after the load time weaving is enabled so it works, more or less, flawlessly for all classes.
With Java Config classes are loaded as soon as the #Configuration annotationed class is loaded. All classes that are imported are loaded into the class loader. After this load time weaving is enabled, however only for classes that are going to be loaded after this point.
Hence the fact that it is working for classes loaded by the configuration as specified for the DispatcherServlet and hence the problem in the ContextLoaderListener.
One thing you can try is to put a #ComponentScan for #Configuration classes on the AppConfig. And let the ContextLoaderListener only load the AppConfig. That might defer the class loading a little until after the load time weaving is enabled.
Something that definitely will work is putting both configuration classes in XML, remove the #EnableLoadTimeWeaving for the AppConfig and use a <context:load-time-weaving />.

How to Setup web application context in Spring MVC test

We have a clear abstraction between Service layers & view layers context configurations and we are loading them as shown below.
Root application context:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
</context-param>
Web application context:
<servlet>
<servlet-name>lovemytasks</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/mmapp-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Now we are trying to introduce SPRING MVC TEST FRAMEWORK to test our application.
For this i would need to setup the same environment as my real web application works.
How can i do that ?
I tried below configuration on my test to load both the contexts.
#ContextConfiguration(locations = { "classpath*:META-INF/spring/applicationContext*.xml",
"file:src/main/webapp/WEB-INF/spring/mmapp-servlet.xml" })
But its erroring out saying
Caused by: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Duplicate <global-method-security> detected.
We have defined global security in both root application context and web application context.
Note: The above said issue will not appear when i run my web application. It happens only when i run Spring MVc test
I tried removing my global security and one place and then landing into errors with conversion services on running my tests. Which warned me that I am not loading the context as teh real Spring application does.
Now, i would like to setup my Spring MVC test environment to use or work as the same way my spring web application environment works. Can any one please suggest how can i achieve it ?
Use the #ContextHierarchy annotation. Its javadoc describes it well. In your case you would use
#WebAppConfiguration
#ContextHierarchy({
#ContextConfiguration(locations = { "classpath*:/META-INF/spring/applicationContext-*.xml" }),
#ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/mmapp-servlet.xml" })
})
don't put your appContext in meta-inf.
The "normal" way is to have one spring-servlet.xml in your web-inf
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/spring-servlet.xml</param-value>
</context-param>
Andn then import different files within the xml file :
<import resource="classpath:beans.xml"/>
I create a seprate appContent for my tests :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations ="classpath:applicationContext-test.xml")
#Transactional
public class MyTest {
Your beans must be getting loaded twice somewhere along the line, are you importing the beans twice, defining them both in xml and also annotating ?

What can cause Spring IoC instantiate more than one instance of a singleton bean per WebApp?

I have a Spring based WebApp. In my application context, I have this bean defined:
<bean id="someSingleton" class="com.fake.SomeSingleton" scope="singleton"/>
I have the one Spring dispatch servlet definition and one class that has the #Controller annotation to which I auto-wired this bean, expecting Spring to only ever instantiating this class once. However, according to the following debug code, Spring is instantiating this class more than once:
private static final Semaphore SANITY_CHECK = new Semaphore(1);
public FakeSingleton(){
if(!SANITY_CHECK.tryAcquire()){
log.error("why?");
System.exit(-1);
else{
log.error("OK");
}
}
What can be the cause?
Note: I use spring 3.1.2.RELEASE
EDIT:
Thanks to the hints I was given, I found the culprit.
Apart from the DispatcherServlet, I also had a ContextLoaderListener in my web.xml. After removing it, SomeSingleton only got instantiated once.
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>FakeService</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
There are few possible reasons:
Your class is wrapped by some CGLIB proxy which causes the constructor to run twice (as opposed to #PostConstruct callback which always runs once per bean) - once for your class and once for inheriting proxy
more likely, your bean is being picked up by two contexts: main one and Spring MVC one. This is a poor practice and you should avoid it. Check out if your SomeSingleton class is not picked up by MVC dispatcher servlet context via some CLASSPATH scanning.
BTW in such a code it's safe to use simple AtomicInteger instead of Semaphore.
A singleton is once per context, not once-per-heat-death-of-the-universe.
Turn on logging and see why/if the entire app context is being created more than once.

spring aop and jersey classes

so in spring xml config, i define the following pointcut:
<aop:config>
<aop:aspect ref="metricsAdviceInterceptor">
<aop:around method="invoke" pointcut="#annotation(com.mycom.MetricsAdvice)"/>
</aop:aspect>
</aop:config>
The idea is to collect metrics on methods that have the "MetricsAdvice" annotation:
class SomeClass {
#MetricsAdvice
public void someMethod(...) { ... }
}
So this all works fine when i explicitly declare the beans in my spring config:
<bean id="someBean" class="com.mycom.SomeClass" />
But i want to be able to use this annotation on jersey code, and it doesn't work. Now, in the jersey config, one adds the below to web.xml. The idea is that you're telling jersey in which packages to find various rest services. Ie, it looks for classes in the packages: com.mycom.restservices.* and instantiates them. Presumably the instantiation of these beans is being done "differently", and thus aren't getting proxied:
<servlet>
<servlet-name>JerseyWebApplication</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<display-name>Jersey Servlet</display-name>
<init-param>
<param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
<param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>
com.mycom.restservices.billing;
com.mycom.restservices.account;
org.codehaus.jackson.jaxrs
</param-value>
</init-param>
....
So what's "best practice" for trying to get these annotations to work on jersey beans?
Thx.
If all you need is to get rid of explicit bean declarations, you can use Spring classpath scanning feature instead of the Jersey one.
Just annotate your Jersey resources with #Component (or other similar annotations), and use <context:component-scan> to specify packages with these resources.
This way beans will be instantiated by Spring, and Spring AOP will work fine.
See also:
3.10 Classpath scanning and managed components

Resources