Created boilerplate project to expose RESTful API to JPA enabled database. It's using the following versions:
- Spring 3.2.6
- Hibernate 4.3.0
- Jersey 2.5.1
I finally was able to get them playing together, but still some question remains. Here's one of the most puzzling things (see excerpt from REST service class)
#Service
#Path("resources")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes({ MediaType.APPLICATION_JSON })
#Transactional
public class ResourceServices extends AbstractServices<Resource> {
...
}
if class is annotated with #Service, #Transactional annotation is ignored and transaction for the methods is not started. However, when changed to #Component, everything works fine. Couldn't figure out, why.
The entire project can be seen here
I got puzzled by this as well, but finally figured this out.
The jersey-spring module will only import #Component beans from your context. There literally is a beanClass.isAnnotationPresent(Component.class) check in SpringComponentProvider.
Otherwise it appears to only create half-baked request-scoped instances of the bean (I traced this with Thread.dumpStack in service constructor). They seem to have dependency injection, but not AOP.
There's a number of JIRA items already in Jersey's issue tracker: JERSEY-2495, JERSEY-2059, JERSEY-2301
UPDATE: My pull request for these has been merged, this should be fixed in Jersey 2.11.
As mentioned in another answer SpringComponentProvider gets a bean created by Jersey and registers it in the Spring context, but in this case you don't get Spring AOP.
I managed to get it working with AOP the other way around: the bean is created by Spring (so in fact it is a proxy because of AOP) and then is registered in Jersey.
But I had to fix a bug in Jersey's ModelHelper class: https://github.com/jersey/jersey/pull/90
Without this fix Jersey was not able to find the #Path annotation in the Spring proxy.
This is the basic structure:
public class MyApplication extends ResourceConfig {
#Inject
public MyApplication(ServletContext servletContext) {
super(JSONController.class, XSSSecurityFilter.class, JacksonFeature.class);
WebApplicationContext springFactory = WebApplicationContextUtils.getWebApplicationContext(servletContext);
// TODO: scan entire Spring factory for beans annotated with #Path and register them, so we don't need to do this manually.
// Letting Jersey register the beans does not work because in this case Spring will not intercept the calls.
register(springFactory.getBean(UserServiceFacade.class));
}
}
The reason is your Spring has a different container for its annotations and jersey has a different container for its annotations , in order to access your beans in spring container you can refer to the below code ;
I beg to differ from the version your using, I haven't tried with the latest version of jersey:
load spring through web.xml like shown below as normal spring confifuration:
<servlet>
<servlet-name>project-spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:project-spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>project-spring</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
Now load your jersey Resources through Application as shown below:
#ApplicationPath("/rest")
public class ResourceLoader extends Application
{
/* (non-Javadoc)
* #see javax.ws.rs.core.Application#getClasses()
*/
#Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> classes = new HashSet<Class<?>>();
loadResourceClasses(classes);
return classes;
}
private void loadResourceClasses(Set<Class<?>> classes)
{
classes.add(StudentResource.class);
}
}
Then in your resource:
#Path("student")
class StudentResource
{
private StudentService studentService;
StudentResource(#Context ServletContext servletContext)
{
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
this.studentService= applicationContext.getBean(StudentService .class);
}
}
You can get your ApplicationContext where all the beans have been initailized using WebApplicationContextUtils of spring pass the servlet context, and get your bean
Related
I have created a new web project from scratch using JSF 2.3 and Weld as CDI implementation running on Tomcat 9. This works all fine.
Now I would like to add a dependency to a service library (Spring 4 + Hibernate 5) which implements e.g. the user authentication against a database. This is all existing code, so it cannot be modified.
I have already read many articles, but unfortunately no one really helped me. I am facing the issue that I do not know how to inject the Spring-Bean (4.3.12.RELEASE) into a JSF-CDI managed bean.
#Named
#RequestScoped
public class AuthenticationController extends AbstractBean {
#Inject
transient private IUserService _springUserService;
#PostConstruct
public void postConstruct() {
// _springUserService is null
}
}
The service implementation class is annotated as
#Repository(IUserService.BEAN_NAME)
public class UserService implements IUserService {
//...
}
In web.xmlI have configured the spring context loader:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring-config-domain.xml
</param-value>
</context-param>
And in faces-config.xml I have configured the Spring-EL-Resolver
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
So which annotation do I need to have the Spring Bean injected?
Am I missing any additional configuration for CDI in order to have it finding the Spring bean?
Thanks in advance and best regards!
I have been learning Spring for just 7 months. While I used spring MVC only, i want to configure custom 404 page by throwing NoHandlerFoundException or enabling it in the dispatcher servlet. Now, i am learning spring boot, can anyone explain to me?
I had the same issue, got it resolved. Below given steps to solve the same.
Create a class GlobalExceptionHandler annotated with #ControllerAdvice
#ControllerAdvice
public class GlobalExceptionHandler
{
#ExceptionHandler(NoHandlerFoundException.class)
public String handleNotFoundError(Exception ex)
{
return "redirect:/yourCustom404page";
}
}
By default, when a page/resource does not exist the servlet container will render a default 404 page. If you want a custom 404 response then you need to tell DispatcherServlet to throw the exception if no handler is found. We can do this by setting the throwExceptionIfNoHandlerFound servlet initialization parameter to true
to achieve this
a. In spring-boot
spring.resources.add-mappings=false in your application.properties or yaml file.
b. If spring-mvc java based configuration is
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
...
#Override
protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext)
{
final DispatcherServlet servlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
servlet.setThrowExceptionIfNoHandlerFound(true);
return servlet;
}
}
c. if spring-mvc xml based configuration, initialize your dispatcher servlet like this
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
The question follows further down this post, but it can be summed up to this : how to get Hibernate Validator 5 to work with RestEasy? Isn't bean validation 1.1 required for JAX-RS 2.0? Or will it work with Bean Validation 1.0)
I give info in this post that go beyond my question, in order to reproduce all steps I did and errors I got on to this point, in the hope it helps anyone, who like me, would like to have a JAX-RS 2.0 (RestEasy impl) on WAS 8.5.5. I found absolutely no doc at all so here it is what I got.
I am currently trying to move from, Jersey 2.xx, JPA 2.1, on Tomcat 7, to RestEasy on WAS 8.5.5. I could also try Apache CXF, but I ran onto other problems.
I can't use Jersey with CDI in WAS because of this : https://java.net/jira/browse/JERSEY-1933
-----THE SETUP----
a) create a ear with a web module in RAD
b) in the deployment descriptor, for the EAR, I set the class loader mode to: parent last, WAR classloader policy : module
c) in the deployment descriptor, for the Web module, I set the class loader mode to: parent last
d) disabled WAS jax-rs engine (jax-rs 1.1) (Added JVM arg -Dcom.ibm.websphere.webservices.DisableIBMJAXWSEngine=true)
note on c) : if I use parent first, JAX-RS 1.1 that comes with WAS gets loaded before my JAX-RS 2.0 jar. All kinds of errors will follow.
note on b) : if i don't use "module", but "application" classloader policy, I get this error
> Provider org.jboss.resteasy.plugins.servlet.ResteasyServletInitializer
> not a subtype
I setup the RestEasy servlet in web.xml
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.mydomain.RestEasyConfig</param-value>
</init-param>
<init-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/rest</param-value>
</init-param>
<init-param>
<param-name>resteasy.servlet.context.deployment</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
The following jar were included in my WAR in the WEB-INF lib
activation-1.1.1.jar
apache-mime4j-0.6.jar
commons-codec-1.6.jar
commons-io-2.1.jar
commons-logging-1.1.3.jar
jackson-annotations-2.4.1.jar
jackson-core-2.4.1.jar
jackson-databind-2.4.1.jar
jackson-jaxrs-base-2.4.1.jar
jackson-jaxrs-json-provider-2.4.1.jar
jackson-module-jaxb-annotations-2.4.1.jar
javax.annotation-api-1.2.jar
javax.el-3.0.0.jar
jaxb-api-2.2.7.jar
jaxb-core-2.2.7.jar
jaxb-impl-2.2.7.jar
jboss-el-api_3.0_spec-1.0.0.Final.jar
jboss-jaxrs-api_2.0_spec-1.0.0.Final.jar
jboss-logging-3.1.4.GA.jar
resteasy-cache-core-3.0.13.Final.jar
resteasy-cdi-3.0.13.Final.jar
resteasy-client-3.0.13.Final.jar
resteasy-jackson2-provider-3.0.13.Final.jar
resteasy-jaxb-provider-3.0.13.Final.jar
resteasy-jaxrs-3.0.13.Final.jar
resteasy-links-3.0.13.Final.jar
resteasy-multipart-provider-3.0.13.Final.jar
resteasy-servlet-initializer-3.0.13.Final.jar
resteasy-validator-provider-11-3.0.13.Final.jar
stax2-api-3.1.1.jar
validation-api-1.1.0.Final.jar
And optionnally
hibernate-validator-5.0.1.Final.jar
resteasy-hibernatevalidator-provider-3.0.13.Final.jar
classmate-0.8.0.jar
I am not using maven for now, before I get a POC (proof of concept) working... so all these jars (exception javax.annotation 1.2) come from the RestEasy 3.0.13 download.
-----THE PROBLEM ------
The problem I run into, is that when ReatEasy tries to load its configuration, it complains it cannot find a validation implementation. For all I know, the container is supposed to come with a bean validation implementation but RestEasy can`t find it.
> Caused by: javax.validation.ValidationException: Unable to create a
> Configuration, because no Bean Validation provider could be found. Add
> a provider like Hibernate Validator (RI) to your classpath. at
> javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:271)
> at
> org.jboss.resteasy.plugins.validation.AbstractValidatorContextResolver.getConfig(AbstractValidatorContextResolver.java:78)
So then I add the 3 optionnal JARs listed above to provide a Hibernate Validator, and removed
resteasy-validator-provider-11-3.0.13.Final.jar
Then I get this error :
class not found : org.hibernate.validator.method.MethodValidator
So I figured that this was a Hibernate Validator 4.3 class which seems not to exists in hibernate validator 5. I went on, removed the Hibernate Validator jars (classmate + hibernate validators 5), and added the following 2 jars :
hibernate-validator-4.3.2.Final.jar
hibernate-validator-annotation-processor-4.3.2.Final.jar
I also removed this jar, as hibernate validator 4.3 implements bean validation 1.0.
validation-api-1.1.0.Final.jar
And then my hello JAX-RS 2.0 service worked!
I was shocked! Doesn't the JAX-RS needs a bean validation 1.1 to work?
How is it that RestEasy ships with Hib. Validator 5.X and the Hib.Validator seems to relies on Hib Validator 4.X?
Does anyone have similar issues with RestEasy?
Any comments?
Let`s see what else break in the next few days! (god I miss Jersey on Tomcat)
extra info as I also post this as a reference for those who would have the same problems...
Final JAR list :
activation-1.1.1.jar
apache-mime4j-0.6.jar
commons-codec-1.6.jar
commons-io-2.1.jar
commons-logging-1.1.3.jar
hibernate-validator-4.3.2.Final.jar
hibernate-validator-annotation-processor-4.3.2.Final.jar
jackson-annotations-2.4.1.jar
jackson-core-2.4.1.jar
jackson-databind-2.4.1.jar
jackson-jaxrs-base-2.4.1.jar
jackson-jaxrs-json-provider-2.4.1.jar
jackson-module-jaxb-annotations-2.4.1.jar
javax.annotation-api-1.2.jar
javax.el-3.0.0.jar
jaxb-api-2.2.7.jar
jaxb-core-2.2.7.jar
jaxb-impl-2.2.7.jar
jboss-el-api_3.0_spec-1.0.0.Final.jar
jboss-jaxrs-api_2.0_spec-1.0.0.Final.jar
jboss-logging-3.1.4.GA.jar
resteasy-cache-core-3.0.13.Final.jar
resteasy-cdi-3.0.13.Final.jar
resteasy-client-3.0.13.Final.jar
resteasy-hibernatevalidator-provider-3.0.13.Final.jar
resteasy-jackson2-provider-3.0.13.Final.jar
resteasy-jaxb-provider-3.0.13.Final.jar
resteasy-jaxrs-3.0.13.Final.jar
resteasy-links-3.0.13.Final.jar
resteasy-multipart-provider-3.0.13.Final.jar
resteasy-servlet-initializer-3.0.13.Final.jar
stax2-api-3.1.1.jar
RestEasyConfig.java
#ApplicationPath("/services")
public class RestEasyConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(TestService.class);
return classes;
}
}
TestService.java
#Path("/test")
public class TestService extends HttpServlet {
private static final long serialVersionUID = 1L;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAccount() {
String ok = "OK!";
Map<String, String> map = new HashMap<String, String>();
map.put("response", ok);
Response response = Response.ok().entity(map).build();
return response;
}
}
The end point : https://localhost:9445/jaxrs2web/rest/test
The result :
{
"response": "OK!"
}
So its turns out that there is 2 providers for bean validation provided with RestEasy.
A) One for Bean Validation 1.0 using absolutely Hibernate Validator 4.X. This has nothing to do with the JAX-RS spec... I guess it just a provider like any other.
B) The other for Bean Validation 1.1, which tries to uses bean validation implementation provider by the conatainer, using either CDI or JNDI.
Context context = new InitialContext();
validatorFactory = tmpValidatorFactory = ValidatorFactory.class.cast(context.lookup("java:comp/ValidatorFactory"));
I need to create my own provider to user my specific (hibernate validator 5.x) Bean Validation 1.1 implementation, as the containers implementation is a 1.0 impl.
This can be done easily using org.jboss.resteasy.plugins.validation.AbstractValidatorContextResolver.java as base code
I am having a problem where the Spring WebApplicationContext appears to be ignoring the #Import annotation on a #Configuration-annotated config class in my web app. It is not a Spring MVC web app.
The problem occurs during processing of the contextInitialized event of a custom ServletContextListener which used to be able to successfully retrieve this bean when I was using XML configuration, but is now not finding this bean now that I have (apparently incorrectly) converted to the use of #Configuration-annotated classes.
The symptoms I see are:
During app startup, I see this output from the Spring framework:
INFO: No annotated classes found for specified class/package
[org.imaginary.spring.config.Instrumented]
Later, when my contextInitialized() method is invoked and I call getBean(), I get a NoSuchBeanDefinition exception
My config classes are factored in such a way that I have, for example, two high-level config classes, one for a "Production" configuration, and another for an "Instrumented" configuration. These top-level config classes are themselves completely empty, but they make use the #Import annotation to (I'm hoping) bring in the relevant bean definitions for that kind of configuration.
For example, here is the Instrumented configuration class:
package org.imaginary.spring.config;
import org.imaginary.spring.config.instrumentation.InstrumentationDependencies;
import org.imaginary.spring.config.servlets.ServletDependencies;
#Configuration
#Import( {InstrumentationDependencies.class, ServletDependencies.class } )
public class Instrumented
{
}
...for the purposes of this example, here is the InstrumentationDependencies config class, defined in a different package:
package org.imaginary.spring.config.instrumentation;
#Configuration
public class InstrumentationDependencies
{
#Bean
public IEventSink eventSinkImpl()
{
return new InstrumentationEventSinkImpl();
}
}
Here is (a stripped-down version of) the contextInitialized() method:
#Override
public void contextInitialized( ServletContextEvent ctxEvent )
{
try
{
if (_publisher == null)
{
WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(ctxEvent.getServletContext());
_eventSink = (IEventSink)springContext.getBean("eventSinkImpl");
}
_eventSink.startReceiving();
}
catch ( Exception e )
{
// handle exception
}
}
Here are the relevant entries from my 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>org.imaginary.spring.config.Instrumented</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.imaginary.MyContextListener</listener-class>
</listener>
Any ideas what I have missed?
In this case, there is nothing wrong with the Spring configuration classes or configuration. The problem was in fact a build issue - the config classes had been added to a package that wasn't getting included in the main web app jar, so the class files weren't present. I was surprised that there wasn't a NoClassDefFoundError exception thrown, as this error leaves the impression that the class exists, it just isn't annotated:
No annotated classes found for specified class/package
[your.class.here].
I'm using Jersey-Spring integration to expose business layer services.
In my web.xml I'm using the SpringServlet:
com.sun.jersey.spi.spring.container.servlet.SpringServlet
My business layer is #Component annotated, so I have #Service's using #Repository's provided via Spring's annotation config.
Repository's are provided to service's via #Autowired annotation.
If I use a repository through a service using my front end MVC classes everithig goes well, but if I use it through Jersey I get a NullPointerException on the repository object.
The version I'm using (through Maven) are:
Spring (and extensions): 3.1.3.RELEASE
Jersey (and extensions): 1.17
There is way to solve this problem using the same version mentioned in your question,
If needed ill mention the second way , the first way is to load sring through web.xml
like shown below as normal spring confifuration:
<servlet>
<servlet-name>project-spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:project-spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>project-spring</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
Now load your jersey Resources through Application as shown below:
#ApplicationPath("/rest")
public class ResourceLoader extends Application
{
/* (non-Javadoc)
* #see javax.ws.rs.core.Application#getClasses()
*/
#Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> classes = new HashSet<Class<?>>();
loadResourceClasses(classes);
return classes;
}
private void loadResourceClasses(Set<Class<?>> classes)
{
classes.add(StudentResource.class);
}
}
Then in your resource:
#Path("student")
class StudentResource
{
private StudentService studentService;
StudentResource(#Context ServletContext servletContext)
{
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
this.transactionService = applicationContext.getBean(StudentService .class);
}
}
There'r you go Spring has been configured with all dependency injections with Jersey!!!
You should try using #InjectParam