spring boot request endpoints return 404 [duplicate] - java

The application uses JDK 8, Spring Boot & Spring Boot Jersey starter and is packaged as a WAR (although it is locally run via Spring Boot Maven plugin).
What I would like to do is to get the documentation I generate on the fly (at build time) as a welcome page.
I tried several approaches:
letting Jersey serving the static contents by configuring in application.properties the proper init parameter as described here
introduce a metadata-complete=false web.xml in order to list the generated HTML document as a welcome-file.
None of that worked out.
I would like to avoid having to enable Spring MVC or creating a Jersey resource just for serving a static file.
Any idea?
Here is the Jersey configuration class (I unsuccessfully tried to add a ServletProperties.FILTER_STATIC_CONTENT_REGEX there):
#ApplicationPath("/")
#ExposedApplication
#Component
public class ResourceConfiguration extends ResourceConfig {
public ResourceConfiguration() {
packages("xxx.api");
packages("xxx.config");
property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}
}
And here is Spring Boot application class (I tried adding an application.properties with spring.jersey.init.jersey.config.servlet.filter.staticContentRegex=/.*html but it didn't work, I'm not exactly sure what the property key should be here):
#SpringBootApplication
#ComponentScan
#Import(DataConfiguration.class)
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Let me just first state, that the reason the static content won't be served is because of the default servlet mapping of the Jersey servlet, which is /*, and hogs up all the requests. So the default servlet that serves the static content can't be reached. Beside the below solution, the other solution is to simply change the servlet mapping. You can do that by either annotating your ResourceConfig subclass with #ApplicationPath("/another-mapping") or set the application.properties property spring.jersey.applicationPath.
In regards to your first approach, take a look at the Jersey ServletProperties. The property you are trying to configure is FILTER_STATIC_CONTENT_REGEX. It states:
The property is only applicable when Jersey servlet container is configured to run as a Filter, otherwise this property will be ignored
Spring Boot by default configures the Jersey servlet container as a Servlet (as mentioned here):
By default Jersey will be set up as a Servlet in a #Bean of type ServletRegistrationBean named jerseyServletRegistration. You can disable or override that bean by creating one of your own with the same name. You can also use a Filter instead of a Servlet by setting spring.jersey.type=filter (in which case the #Bean to replace or override is jerseyFilterRegistration).
So just set the property spring.jersey.type=filter in your application.properties, and it should work. I've tested this.
And FYI, whether configured as Servlet Filter or a Servlet, as far as Jersey is concerned, the functionality is the same.
As an aside, rather then using the FILTER_STATIC_CONTENT_REGEX, where you need to set up some complex regex to handle all static files, you can use the FILTER_FORWARD_ON_404. This is actually what I used to test. I just set it up in my ResourceConfig
#Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("...");
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}

For anyone who still can't get this to work, I followed the answer provided by #peeskillet, and had to make an additional change.
Previously I had created the following method in Application.java.
#Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfig.class.getName());
return registration;
}
The problem is that this registered the servlet for the /* path, and then setup the Jersey ResourceConfig configuration file.
Once I removed the above method, and placed the #Configuration annotation on my ResourceConfig class, I noticed the static resource could be retrieved via Spring Boot.
For completeness, this is a snippet of my ResourceConfig now.
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
// Application specific settings
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
This blog post was helpful in determining the difference approach for the ResourceConfig.

Below setup worked for me
Set
spring .jersey.type: filter
set FILTER_FORWARD_ON_404
#Configuration
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig () {
try {
register(XXX.class);
property(ServletProperties.FILTER_FORWARD_ON_404, true);
} catch (Exception e) {
LOGGER.error("Exception: ", e);
}
}
}
Note: Use #Configuration instead of #component

Related

How to access Rest APIs mentioned in Controller class of a Jar file in my Spring Boot Application

I am new to Spring / Spring Boot.
In my Spring Boot Application, I have a JAR file which has a Controller class and corresponding Service Interface (not class). I have implemented the Service interface and created a Service class with some logic. Now I want to access the APIs (/v1/getDetails) present in Controller class of JAR file.
Can we access the REST APIs present in Controller class of JAR file? If so then please guide me.
PS: I have tried to search on internet but didn't get a clear answer.
Controller class in Jar:
#RestController("/v1")
public class BasicRestController {
#Autowired
BasicServiceInterface basicService;
#GetMapping("/supportedDeviceTypes")
public NameValuePair<?> getSupportedDeviceTypes() {
return basicService.getSupportedDeviceTypes();
}
}
Spring Boot Main Class:
#SpringBootApplication
public class XXXProjectApplication{
public static void main(String[] args) {
SpringApplication.run(XXXProjectApplication.class, args);
}
}
Configuration Class:
#Configuration
public class CommonControllerConfig {
#Bean
public BasicRestController basicRestController() {
return new BasicRestController();
}
}
Accessing http://localhost:8080/v1/supportedDeviceTypes but getting - 'This application has no explicit mapping..' in browser.
But I have printed the loaded beans in Application Context and could see BasicRestController bean is loaded while starting the app.
Try removing following bean from CommonControllerConfig
#Bean
public BasicRestController basicRestController() {
return new BasicRestController();
}
And instead add #ComponentScan(basePackages = "your.controller.package")
Also, as JB Nizet mentioned, it should be #RestController #RequestMapping("/v1") in BasicRestController.

filenotfoundexception running jerseyTest - missing applicationContext.xml

I'm running a test on Hello World trying to follow the Jersey Framework in my spring java program.
I have extended JerseyTest, but I'm getting the error above. The example Jersey gives link doesn't seem to help.
public class SimpleTest extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(RestService.class);
}
#Test
public void test() {
final String hello = target("\service\hello").request().get(HelloModel.class);
assertEquals("Hello World!", hello);
}
}
With Jersey 2.26 the dependency has changed to jersey-spring4, but to flush out the answer a little bit more if you don't want to spin up the entire Spring context.
Create the object:
final ResourceConfig resourceConfig = new ResourceConfig(restResource);
In my case, I just needed an empty context:
final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
resourceConfig.property("contextConfig", context);
That was able to allow the JerseyTest to run successfully.
This is going to happen if you have the jersey-spring3 dependency. The default behavior is to look for this applicationContext.xml file (which is your Spring context configuration).
If you want to configure Spring for the test, you can do a couple things.
You could manually create the ApplicationContext and pass it to Jersey
ApplicationContext context = ...
return new ResourceConfig(RestService.class)
.property("contextConfig", context)
If you are using xml configuration, then you would create a ClassPathXmlApplicationContext. If you're using Java config, then you would create an AnnotationConfigApplicationContext.
If you need servlet servlet container support, check out the example in this post

JerseyTest unsatisfied dependency (Dependency Injection)

I need to test my RESTful service and I thought the most appropriate way to do it would be using the Jersey Test Framework since I am already using Jersey for my service.
However, when running a simple test I am getting a UnsatisfiedDependencyException because the implementation of the DAO I am using in the service is not found (it's being set via DI). This error used to happen to me everytime I modified my pom.xml Maven configuration file, mostly when adding or removing dependencies. Supposedly, the IDE I'm using, IntelliJ IDEA is supposed to regenerate the artifacts automatically, but for me to avoid this dependency failure I needed to remove the old artifact from the Tomcat server deployment options as well as from the project structure, and add it again.
The problem here is that the test framework is using a separate server, as the documentation said. I am using the following one:
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.23.1</version>
</dependency>
I don't know how to solve the dependency issue if I am using a different server.
The top error message in the exception stack says:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no
object available for injection at SystemInjecteeImpl
Any ideas?
Edit. I managed to patch it with a dirty solution.
First, let me show you how I was injecting the DAO:
// Binder.java
public class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(TournamentDao.class).to(ITournamentDao.class).in(Singleton.class);
}
}
// DependencyInjectionFeature.java
#Provider
public class DependencyInjectionFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(new Binder());
return true;
}
}
// EventSchedulerService.java
#Path("eventscheduler")
public class EventSchedulerService {
private ITournamentDao dao;
#Inject
public EventSchedulerService(ITournamentDao tournamentDao) {
dao = tournamentDao;
}
}
The web configuration file web.xml scans the package where these classes are placed (the package has the same name than the one where the test exists) and perform the dependency injection since it finds the #Provider annotation.
To patch my issue I did the exact same thing in the test class:
public class EventSchedulerServiceTest extends JerseyTest {
protected Application configure() {
ResourceConfig config = new ResourceConfig(EventSchedulerService.class);
config.register(new AbstractBinder() {
#Override
protected void configure() {
bind(TournamentDao.class).to(ITournamentDao.class).in(Singleton.class);
}
});
return config;
}
}
Obviously this is a bad idea because now I have duplicate code; but it did the trick. I'd like to know how to make the test server to use the service configuration correctly.
I remember from a previous discussion in the comments here, you had the problem of not using a ResourceConfig to configure your main application. You we instead using web.xml.
The reason you are using a Feature annotated with #Provider is that you we're using package scanning in your web.xml
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>your.packages.to.scan</param-value>
</init-param>
The package scan picked up the #Provider on the Feature, and automatically registers it. This provider is what is used to register the Binder, which is what configured the dependency injection.
As mentioned, a Jersey application can be configured in a ResourceConfig or in a web.xml. Anything jersey configuration you can do in the web.xml, you can also do in the ResourceConfig. There are a bunch of different ways you can register components with the ResourceConfig
You can just call register
config.register(new DependencyInjectionFeature());
You can also package scan, the same way you are doing in the web.xml, by calling packages
config.packages("your.packages.to.scan");
You should also look at your implementation of the Binder class. It extends AbstractBinder. The look at how you are currently configuring the DI in the test
config.register(new AbstractBinder() {
#Override
protected void configure() {
...
}
});
You are registering an AbstractBinder instance. So you know that calling you can register the DI configuration calling register on the ResourceConfig, so you could instead of registering a different binder, just register an instance of the Binder class
config.register(new Binder());
So you have some options.

Spring: ResourceResolver not called from error page

I have a Spring Boot web application that adds a resource handler for static resources (mainly CSS and JS files) using the addResourceHandlers method of WebMvcConfigurerAdapter. This resource handler is further configured to enable a VersionResourceResolver Basically this is
public class CustomWebMvcConfigurer extends WebMvcConfigurerAdapter {
...
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
ResourceHandlerRegistration registration = registry.addResourceHandler(...)
ResourceChainRegistration chainRegistration = registration.resourceChain(...);
chainRegistration.addResolver(
new VersionResourceResolver().addContentVersionStrategy(...));
...
}
}
Now the problem is, when the user sees an error page (for example the 404 page), this VersionResourceResolver won't be used. (The error pages are configured using #ExceptionHandlers in a #ControllerAdvice annotated class.) The error pages will be rendered and displayed fine, however, the resources loaded on this page don't have version applied.
The Spring Boot documentation has a note saying that an ERROR dispatcher needs special treatment, however, I cannot figure out how to apply this advice in the context of our WebMvcConfigurerAdapter.
Any help is appreciated.
I had the same problem in a my opensource project and I resolve in this way.
I create an #Controller class like this:
#Controller
public class ExceptionController {
#Autowired
private MessageSource messageSource;
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
#RequestMapping("/exception")
public String exception(Model model,Exception ex,Locale locale,HttpServletRequest httpRequest,HttpServletResponse httpResponse){
ex.printStackTrace();
model.addAttribute("templatePath", "exception/exception");
model.addAttribute("template", "content");
try{
model.addAttribute("exceptionMessage",messageSource.getMessage(String.format("exception.body.%s",httpResponse.getStatus()),new Object[]{},locale));
} catch (NoSuchMessageException e){
model.addAttribute("exceptionMessage",messageSource.getMessage("exception.body",new Object[]{},locale));
}
return "index";
}
}
and then I customize the servlet container configuration in this way:
#Bean
public EmbeddedServletContainerCustomizer exceptionHandling() {
return container -> container.addErrorPages(new ErrorPage("/exception"));
}
in feew words I make a special page for error /exception in this case and then configure the embedded tomcat for use this url in case of exceptions
In the Autoconfiguration classes of Spring boot you can see in org.springframework.boot.autoconfigure.web.BasicErrorController this documentation:
Basic global error {#link Controller}, rendering {#link ErrorAttributes}. More specific
* errors can be handled either using Spring MVC abstractions (e.g.
* {#code #ExceptionHandler}) or by adding servlet
* {#link AbstractEmbeddedServletContainerFactory#setErrorPages container error pages}
I hope that tis can help you

Use different paths for public and private resources Jersey + Spring boot

I'm using Spring boot + Jersey + Spring security, I want to have public and private endpoints, I want an schema as follow:
/rest -- My root context
/public -- I want to place my public endpoints in this context, It must be inside of the root context like /rest/public/pings
/private -- I want to place my private endpoints in this context, It must be inside of the root context like /rest/private/accounts
I have my configuration as follow:
Jersey configuration:
#Configuration
#ApplicationPath("/rest")
public class RestConfig extends ResourceConfig {
public RestConfig() {
register(SampleResource.class);
}
}
Spring security configuration:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
........
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/rest/public/**").permitAll();
http.antMatcher("/rest/**").authorizeRequests().anyRequest().fullyAuthenticated().and().httpBasic();
http.csrf().disable();
}
}
The question is how can I register two application paths inside of my /rest context, one for /public and the other one for /private ?
NOTE: I tried to create another ResourceConfig as follow:
#Configuration
#ApplicationPath("/rest/public")
public class RestPublicConfig extends ResourceConfig{
public RestPublicConfig() {
register(PingResource.class);
}
}
But I'm getting the next error:
No qualifying bean of type [org.glassfish.jersey.server.ResourceConfig] is defined: expected single matching bean but found 2: restConfig,restPublicConfig
Thanks for your help :)
In a servlet container, the Jersey runtime, runs as either a servlet or as a servlet filter. How spring boot configures servlets and filters is through ServletRegistrationBeans and FilterRegistrationBeans, respectively. To get an idea of how that configuration works behind scenes, you can look at the source code for the JerseyAutoConfiguration
In the JerseyAutoConfiguration, you can see that a ResourceConfig is injected, and that is the ResourceConfig used to create the Jersey servlet or Jersey filter (depending on your choice of configuration). So the reason for the error is that you can't have ambiguous beans, which you have two ResourceConfig beans. So Spring doesn't know which one to inject.
What you can do though, is use two different servlets for each ResourceConfig. The problem is that Spring Boot only hooks you up with one servlet for Jersey, so you need to configure the other one yourself. There are two options:
Use the Spring Boot auto-configuration for one of the Jersey applications, and add another ServletRegistrationBean for your other one. The one thing to note is that the ResourceConfig for your created ServletRegistrationBean should not be a Spring component (i.e. no #Component or #Configuration), or else you will still face the same error.
public class PublicConfig extends ResourceConfig {
public PublicConfig() {
register(PingResource.class);
}
}
...
// in your Spring Boot configuration class
#Bean
public ServletRegistrationBean publicJersey() {
ServletRegistrationBean publicJersey
= new ServletRegistrationBean(new ServletContainer(new PublicConfig()));
publicJersey.addUrlMappings("/rest/public/*");
publicJersey.setName("PublicJersey");
publicJersey.setLoadOnStartup(0);
return publicJersey;
}
Don't use the Spring Boot configuration at all. Just create two ServletRegistrationBeans. In this case, none of your ResourceConfig classes should be Spring beans.
#Bean
public ServletRegistrationBean publicJersey() {
ServletRegistrationBean publicJersey
= new ServletRegistrationBean(new ServletContainer(new PublicConfig()));
publicJersey.addUrlMappings("/rest/public/*");
publicJersey.setName("PublicJersey");
publicJersey.setLoadOnStartup(0);
return publicJersey;
}
#Bean
public ServletRegistrationBean privateJersey() {
ServletRegistrationBean privateJersey
= new ServletRegistrationBean(new ServletContainer(new PrivateConfig()));
privateJersey.addUrlMappings("/rest/private/*");
privateJersey.setName("PrivateJersey");
privateJersey.setLoadOnStartup(1);
return privateJersey;
}
Personally, I prefer the second option, as it is easier to reason about the configurations when they are all in one place.
Another thing to note is that the two Jersey applications will be completely independent, meaning you will need to register providers (like filters) for both applications
You won't be allowed to create two beans for your Resource Class. You can achieve what you are trying to achieve using a single Resource Class as well.
Here is an example:
#Path("rest")
public class SampleResourceClass {
#Path("/public/pings")
#GET
public Responce getPings(){
/* Code Here */
}
#Path("/private/accounts")
#GET
public Response getAccounts(){
/* Code Here */
}
}
The error you are seeing is not related to your security config, you may want to take a look at this ticket, https://github.com/spring-projects/spring-boot/issues/3260
If you want to permit all traffic to endpoints past /public you can add the RequestMatcher to the Spring Security ignore list.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/rest/public/**");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatcher("/rest/private/**")
.anyRequest().authenticated().and()
.httpBasic().and()
.csrf().disable()
}
}
http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc

Categories

Resources