#Provider AND Registration in Application(ResourceConfig) requirement for custom MessageBodyWriter/Reader? - jersey

Is there a requirement that a custom MessageBodyWriter and MessageBodyReader must not only be annotated by #Provider annotation AND also be included in the Application configuration through Application or ResourceConfig. If yes, why?
My train of thought was that, if classes are annotated with #Provider then the JAX-RS runtime by default loads such classes as it's runtime component. Declaring the same class inside the Application makes is a redundant exercise. It appears my train of thought is wrong, but I am looking for some kind of explanation on how and why this has been designed this way(ie both annotation and Application configuration).
I can understand that some form of Application configuration would be required on the Jersey Client side, but am not very confident about that either.
For instance, the JavaMarshaller class below has been annotated with #Provider
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.Provider;
#Provider
#Produces("application/example-java")
#Consumes("application/example-java")
public class JavaMarshaller implements MessageBodyReader, MessageBodyWriter {
.......
Now in the Application class is it required to do as below?
#ApplicationPath("services")
public class ShoppingApplication extends Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> classes = new HashSet<Class<?>>();
public ShoppingApplication() {
classes.add(JavaMarshaller.class);
}
.........

Typically, the registration of classes dynamically is a feature of application servers. Since your'e deploying to tomcat, Jersey will likely expect that you're listing your providers and resources in your Application class.
If you were deploying to a full app server, like WildFly or GlassFish, you wouldn't need to do this.

Related

How to set different prefix for endpoints and for static content in spring boot?

I would like to ask if it is possible (and how) to set different prefixes for static content and for endpoints in the spring boot app. I have been using server.servlet.contextPath=/api in application properties but it also sets the prefix for static content.
What I would like to achieve is to have "/api" prefix for endpoints in controllers and to serve the static content from "/" root.
EDITED
Actually what I need is to be able to set only the static content to "/" and rest of the app be available on "/api"
Spring Boot uses the built-in WebMvcAutoConfigurationAdapter for a Webapplication. The default behavior is defined within this class.
I assume your controllers are annotated with #RestController. If this is the case, you're able to map all controllers to a different location.
Create a configuration class which implements the WebMvcConfigurer interface. Override the method configurePathMatch(PathMatchConfigurer configurer) and implement your desired path configuration.
In the following example, the static content will still be served at "/" and all REST endpoints will be accessible with "/api" as prefix.
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerTypePredicate;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class));
}
}
You'll notice there's no #EnableWebMvc annotation in this class, because it would disable the Spring Boot autoconfiguration mechanism for WebMvc.

Spring boot custom starter and Spring Data JPA - How to correctly provide repositories on my own custom autoconfigure/starter module

I am trying to write an autoconfigure / starter module for one of my project. This module handles the persistence through Spring Data JPA. It aims at providing several spring data repositories.
Right now, my autoconfiguration looks like this:
#Configuration(proxyBeanMethods = false)
#AutoConfigureAfter(JpaRepositoriesAutoConfiguration::class)
#EnableJpaRepositories(basePackageClasses = [ItemRepository::class])
#EntityScan(basePackageClasses = [ItemRepository::class])
class DomainPersistenceDataJpaAutoConfiguration() {
}
As stated in spring boot reference documentation, auto configuration should not enable component scanning, although #EnableJpaRepositories uses component scanning.
What would be a good alternative approach? Is there any example of existing spring boot start which provides repositories implementation I could consult?
What you're looking for is probably
AutoConfigurationPackages#register method, I think the common approach would be to implement ImportBeanDefinitionRegistrar and import this implementation in your autoconfiguration using #Import. A very elegant solution can be seen in Axon framework's RegisterDefaultEntities annotation and DefaultEntityRegistrar which it imports. This way your packages will be included in jpa and entity scans.
Edit: Adding actual example since as the review pointed out, the links might change over time.
In your case the ImportBeanDefinitionRegistrar could look something like this:
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class StarterEntityRegistrar implements ImportBeanDefinitionRegistrar {
#Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, ItemRepository.class.getPackageName());
}
}
and your autoconfiguration would be just:
#Configuration(proxyBeanMethods = false)
#AutoConfigureAfter(JpaRepositoriesAutoConfiguration.class)
#Import(StarterEntityRegistrar.class)
class DomainPersistenceDataJpaAutoConfiguration() {
}

How is #ConfigurationProperties-annotated classes detected automatically with #SpringBootApplication Annotation

I am learning Spring Boot and have a question with one example in the reference documentation.
Following section of the documentation mentions
6. Using the #SpringBootApplication Annotation
A single #SpringBootApplication annotation can be used to enable those
three features, that is:
#EnableAutoConfiguration: enable Spring Boot’s auto-configuration
mechanism
#ComponentScan: enable #Component scan on the package where the
application is located (see the best practices)
#Configuration: allow to register extra beans in the context or import
additional configuration classes
and the following example to replace this single annotation by any of the features that it enables is bit confusing for me . The example
package com.example.myapplication;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
#Configuration(proxyBeanMethods = false)
#EnableAutoConfiguration
#Import({ MyConfig.class, MyAnotherConfig.class })
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Explanation for the example
In this example, Application is just like any other Spring Boot
application except that #Component-annotated classes and
#ConfigurationProperties-annotated classes are not detected
automatically and the user-defined beans are imported explicitly (see
#Import).
The only major difference I see in the example code above is that it does not have #ComponentScan annotation. I also read in the comments section of an SO answer (Stephane Nicoll May 5 '17 at 11:07) that #Component annotation is not recommended officially to auto detect #ConfigurationProperties. So my assumption is that Spring framework classes with #ConfigurationProperties are not annotated with #Component.
Also I checked the #SpringBootApplication annotation source and couldn't identify anything that should enable the automatic detection of #ConfigurationProperties annotated classes.
The reference document 2.8.3. Enabling #ConfigurationProperties-annotated types section shows the following way to scan and autodetect #ConfigurationProperties
#SpringBootApplication
#ConfigurationPropertiesScan({ "com.example.app", "org.acme.another" })
public class MyApplication {
}
With all these details , I would like to understand
Why is it explicitly mentioned for this example that #ConfigurationProperties-annotated classes are not detected automatically ? and How is #ConfigurationProperties annotated classes automatically detected when #SpringBootApplication is used.
Additional note : I saw a small difference between the prior version of the documentation and the current one. The following reference is missing the current one
Keep in mind that the #EnableConfigurationProperties annotation is
also automatically applied to your project so that any existing bean
annotated with #ConfigurationProperties is configured from the
Environment
Following is what I understand from my analysis.
#ConfigurationProperties annotated types can be registered to the ApplicationContext by
Annotating the class with #ConfigurationProperties with an
annotation that falls in the scope of #ComponentScan (
#Component, #Service and the like ) . Not recommended as per the comment from Stephane Nicoll , which makes sense to me now.
Using annotation
#EnableConfigurationProperties . For this to
work the class annotated with #EnableConfigurationProperties
should be annotated with an annotation that falls in the scope of
#ComponentScan ( #Component, #Service and the like )
Using annotation #ConfigurationPropertiesScan and by making sure
the classes annotated with #ConfigurationProperties is placed
under its radar. The usage is similar to #ComponentScan .
Now , when we replace #SpringBootApplication with individual annotations that it enables and omit #ComponentScan (as in example) , the #EnableConfigurationProperties way (Point 2) of registering the types with #ConfigurationProperties will not work. This probably answers both my questions on why and how .
This was explicitly mentioned probably because the connection between #EnableConfigurationProperties and #ComponentScan is not that obvious for people like me.
Additional details.
The registration of #ConfigurationProperties annotated types when we use #EnableConfigurationProperties happens through of the EnableConfigurationPropertiesRegistrar class that is imported by the annotation
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Import(EnableConfigurationPropertiesRegistrar.class)
public #interface EnableConfigurationProperties {..}

Spring MVC, Servlet 3.0: how to create resource-ref

I am developing a web application with Spring MVC (4.2.3) and
Servlet 3.0 API, so there is no web.xml.
My WebConfig.java is as follows:
...
import javax.servlet.ServletContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {...})
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
ServletContext servletContext;
}
I am creating this spring application by copying from a java application with
Servlet < 3.0, so there is a web.xml which contains this section regarding
the data source:
<resource-ref>
<res-ref-name>jdbc/DefaultDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
</resource-ref>
How do I create such a setting in my Spring MVC application where there is no
web.xml?
In the meantime I have had a look at the "Java Servlet Specification Version 3.0".
It says about #Resource:
The #Resource annotation is used to declare a reference to a resource
such as a data source... This annotation is equivalent to declaring a
resource-ref...
#Resource example:
#Resource private javax.sql.DataSource catalogDS;
public getProductsByCategory() {
// get a connection and execute the query
Connection conn = catalogDS.getConnection();
..
}
In the example code above, a servlet, filter, or listener declares a
field catalogDS of type javax.sql.DataSource for which the reference
to the data source is injected by the container prior to the component
being made available to the application.
The data source JNDI mapping is inferred from the field name
“catalogDS” and type (javax.sql.DataSource). Moreover, the catalogDS
resource no longer needs to be defined in the deployment descriptor.
Unfortunately I don't know how to use it and how to get it connected to Springs JDBCTemplate. Is
public class WebConfig extends WebMvcConfigurerAdapter {
the right location at all?
I implemented a listener by extending
org.springframework.web.context.ContextLoaderListener
and added the annotation
#WebListener.
In that listener the field
#Resource private javax.sql.DataSource catalogDS;
is successfully populated.
http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContextListener.html
http://docs.oracle.com/javaee/6/api/javax/servlet/annotation/WebListener.html
http://docs.oracle.com/javaee/6/api/javax/annotation/Resource.html

HelloWorld EJB dependency injection

I cannot get #EJB dependency injection to work. I use Linux and Maven as a build tool.
For the source code, IDE and app server I like to use the alternatives that makes it as simple as possible. I gave it a try with Glassfish 3.1.2.2, NetBeans 7.2 but no luck. I haven't done any configuration in Glassfish.
Here is some example source code, but any code that works will be helpful. Also any ideas about how to debug these kind of problems will be appreciated. It seems like every beginner is having them...
LinkResource.java
package se.xyz.webapp;
import javax.ejb.EJB;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import se.xyz.server.LinkService;
#Path("links/{username}")
public class LinkResource {
#EJB
LinkService service;
#GET
#Produces("text/plain")
public String link(#PathParam("username") #DefaultValue("NoName") String name ) {
return service.store(name); // Always nullpointer exception here!
}
}
LinkServiceImpl.java
package se.xyz.server;
import javax.ejb.Stateless;
#Stateless
public class LinkServiceImpl implements LinkService {
public String store(String name)
{
return "From eJB";
}
}
LinkService.java
package se.xyz.server;
import javax.ejb.Local;
#Local
public interface LinkService {
public String store(String name);
}
The webapp is showing but the variable service is always null. It's not so important to get this code to work, but just if I could get any DI to work. In a distant future I like to persist too, however I would like to do it manually instead of getting a huge working app from an architype. My goal is to understand what I'm doing... Any help would be appreciated.
The problem is that the class in which you are trying to inject is a JAX-RS resource.
JAX-RS resources are a bit of an oversight in Java EE where it concerns the alignment of managed bean types. When Java EE 6 was created it just happened to be that JAX-RS (and JSF 2) finished early, while CDI and the overarching "managed bean" concept finished late.
JAX-RS is a container managed type of bean, but unfortunately not of the official "managed bean" variety, and it thus does not support #EJB directly.
You can make it a CDI managed bean and then use #Inject instead of #EJB.
If you want to inject a reference to your LinkService EJB into the LinkResource resource, then your LinkResource must be a managed component, in other words a stateless session bean. If you add a #Stateless annotation in your LinkResource class, you should be fine.
Context and Dependency injection needs to be enabled per project. In netbeans right click your project and choose New > Other
Under categories select Contexts and Dependency injection then select beans.xml under File types.
Then click Next and then Finish.
Or you can manually create the beans.xml file in Web Pages/WEB-INF folder. Contents should be:
<?xml version="1.0" encoding="UTF-8"?>
<beans 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/beans_1_0.xsd">
</beans>

Resources