Serving static index without viewresolver - spring-boot

I want to build a basic project with two controllers, one serving static pages and the other one with REST endpoints defined. I don't need any view resolver for that so that's what I put together:
#SpringBootApplication
#Controller
public class MyAppApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(MyAppApplication.class, args);
}
#RequestMapping("/")
public String index() {
return "index";
}
}
And My pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
my index.html is under src/main/resources/ and I've tried to return index, index.html as well as full path but I still end up getting 404 error.
Am I missing or misunderstanding something?

Serving static content:
By default Spring Boot will serve static content from a directory called /static (or /public or /resources or /META-INF/resources) in the classpath or from the root of the ServletContext. It uses the ResourceHttpRequestHandler from Spring MVC so you can modify that behavior by adding your own WebMvcConfigurerAdapter and overriding the addResourceHandlers method.
In a stand-alone web application the default servlet from the container is also enabled, and acts as a fallback, serving content from the root of the ServletContext if Spring decides not to handle it. Most of the time this will not happen (unless you modify the default MVC configuration) because Spring will always be able to handle requests through the DispatcherServlet.
By default, resources are mapped on /** but you can tune that via spring.mvc.static-path-pattern. For instance, relocating all resources to /resources/** can be achieved as follows
spring.mvc.static-path-pattern=/resources/**
You can also customize the static resource locations using spring.resources.static-locations (replacing the default values with a list of directory locations). If you do this the default welcome page detection will switch to your custom locations, so if there is an index.html in any of your locations on startup, it will be the home page of the application.
DispatcherServlet maintains a list of implementations to use by default. This information is kept in the file DispatcherServlet.properties in the package org.springframework.web.servlet.
For example it’s quite common to configure an InternalResourceViewResolver settings its prefix property to the parent location of view files.
Regardless of the details, the important concept to understand here is that once you configure a special bean such as an InternalResourceViewResolver in your WebApplicationContext, you effectively override the list of default implementations that would have been used otherwise for that special bean type. For example if you configure an InternalResourceViewResolver, the default list of ViewResolver implementations is ignored.
http://docs.spring.io/spring/docs/5.0.0.RC2/spring-framework-reference/web.html#mvc-servlet-config
See the example in github

Related

Can properties file be customised in spring boot?

I am new to spring boot. Spring boot has default properties file located in /src/main/resource folder with name application.properties. Now I want not to use the default property.
Suppose if I put two properties file:
1. demo1.properties
2. demo2.properties
How will be those two properties file referred in spring boot?
I am trying to use #PropertySource in main class but it is not working. Is there any other way?
This is my main class::
#SpringBootApplication
#ComponentScan("com.wiley.dnb.controller")
#PropertySource("classpath:log4j.properties")
#PropertySource("classpath:services.properties")
public class DNBMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
SpringApplication.run(DNBMain.class, args);
}
}
Thanks in advance.
You need spring profiles!
Profiles help to have different application configuration for different environments.
At startup of your program, you choose which profile/configuration to use.
Here is a tutorial on this subject:http://www.springboottutorial.com/spring-boot-profiles
Add this dependency;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
And add #PropertySource("classpath:application.properties") into your configuration Class.
You could annotate a configuration class with :
#PropertySource({"classpath:demo1.properties", "classpath:demo2.properties"})
to add properties in the Spring environment.
For example :
#SpringBootApplication
#PropertySource({"classpath:demo1.properties", "classpath:demo2.properties"})
public class MyApplication { ...}
Of course these properties file have to be located in the classpath at runtime.

Using a RestTemplate in a non Web app in SpringBoot app

I have a Spring Boot app that is not a web app, that has this piece of code
ResponseEntity<GeolocationAddress> response = new RestTemplate().getForEntity(urlStringConnection,
GeolocationAddress.class);
But then I have this error:
The import org.springframework.web cannot be
resolved
so I added this dependency
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.0.2.RELEASE</version>
</dependency>
But then when I start the app I got this error:
Could not evaluate condition on org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration due to org/springframework/web/context/support/StandardServletEnvironment not found. Make sure your own configuration does not rely on that class. This can also happen if you are #ComponentScanning a springframework package (e.g. if you put a #ComponentScan in the default package by mistake)
I may be a little late, but I had similar issues. By default Spring Boot tries to deduce the type of the application context to use by examining the class path. If it finds either javax.servlet.Servlet or org.springframework.web.context.ConfigurableWebApplicationContext, it instantiates a WebApplicationContext.
To avoid the error you got, I had to do the following in the main method of the app:
#SpringBootApplication
public class App {
public static void main(String[] args) {
new SpringApplicationBuilder(App.class)
.contextClass(AnnotationConfigApplicationContext.class).run(args);
}
}
I also excluded any unwanted AutoConfigure classes in the application.properties :
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration,org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration

Following Spring 5 webmvc Rest service guide - No converter found for return value of type

I am following the official guide for Building a RESTful Web Service from Spring.io
In short, I'm following the above guide but using Tomcat to deploy and execute.
I've looked at some SO questions and answers regarding this issue.
Spring Boot Application: No converter found for return value of type
and some others.
I have the getter methods in the Greeting class though I don't have setters. This is exactly how the class is on the guide.
I tried adding the fasterxml json dependency on my pom.xml but the error message is the same.
All my setup is exactly the same as the guide except the app is bootstrapped by a dispatch-servlet.xml instead of a main method.
web.xml
<servlet>
<servlet-name>greeting</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>greeting</servlet-name>
<url-pattern>/greeting/*</url-pattern>
</servlet-mapping>
greeting-servlet.xml
<context:component-scan base-package="com.test" />
This is it. All the other classes are written exactly the same as the guide. with #RestController and no #ResponseBody as #RestController is rolled out with #Controller and #ResponseBody according to the guide itself.
This is my error message.
WARNING: Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class com.test.Greeting
I don't think it's the issue with getters( I definitely have them).
Nor it's the issue with not having the fasterxml dependency.
What am I missing?
You definitely need the main method:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The #SpringBootApplication annotation does more than you think. The most important thing is that it calls#EnableAutoConfiguration. This is used to figures out which other dependencies you do in your project. In your case that annotation would configure Jackson to parse your Java object to JSON.
Directly from the JavaDoc:
Enable auto-configuration of the Spring Application Context,
attempting to guess and configure beans that you are likely to need.
Auto-configuration classes are usually applied based on your classpath
and what beans you have defined. For example, If you have
tomcat-embedded.jar on your classpath you are likely to want a
TomcatEmbeddedServletContainerFactory (unless you have defined your
own EmbeddedServletContainerFactory bean).
If you still think creating a war file is a good idea, check if this can help you setup Jackson with plain Spring.
EDIT: How to run Spring Boot without Tomcat
You could check this out, the doc says to do this:
..
<packaging>war</packaging>
..
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
And in theory you should be good to go, BUT that might not be the case so check out the sample project from Spring.
These people also got some issues, so check out if it also effects you.

How to inject a bean into JAX RS resource?

I am sure this is most probably a silly question but I am not familiar with JAX RS (and Jersey).
We've had a standalone Java application that basically starts a RESTful service. As part of a refactoring, we've moved this application to be just a thread within another application. That other application uses Spring beans that are defined in an application-context.xml. So, I need to inject some of those beans to the resource class (if that's the correct name for it: the one with #Path annotations, etc.). The problem is I don't know what instantiates this particular class. There is a main class of the legacy app that is creating a (jetty) Server instance with ServletContexthandler to which a ServletHolder is added to which a ResourceConfig is set. Something like that.
So, I can inject my stuff from Spring to this main class but can't see how exactly I can pass those objects to the JAX RS resource?
I am sure I miss something pretty simple.
Edit: I have added a better explanation to my problem and a solution I found below.
Jersey has integration with Spring support. For this case, there are really only two things you need to do:
Make sure you have the integration dependency. You'll also need to the commons logging, so it doesn't complain
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring4</artifactId>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
Just add a ContextLoaderListener along with a WebApplicationContext containing your Spring context configuration.
ServletContextHandler context
= new ServletContextHandler(ServletContextHandler.SESSIONS);
AnnotationConfigWebApplicationContext wac
= new AnnotationConfigWebApplicationContext();
wac.register(SpringConfig.class);
context.addEventListener(new ContextLoaderListener(wac));
Here the SpringConfig is just a "Java config" Spring configuration class. If you wanted you could use an XML application context, but the example I used in the below link uses a Java config class, but also show how to easily import an XML into the class if you just want to use your XML config. You can combine two.
That's pretty much it. Once you have this configured, you should be able to #Autowired your Spring beans into your Jersey resources.
For a complete example, check out this GitHub repo
Maybe I wasn't able to explain well my problem, so basically it was a problem of how to inject beans into JAX-RS resource classes when the actual JAX-RS app is not being instantiated through its own DI-mechanism but from somewhere else. In my case I already had a Spring container that creates those beans and there was no easy way to link the Spring's own bean application context to the JAX-RS's one. A better solution would have been the one already answered but additional problem is that our existing Spring solution is XML-based, whereas the #Injected annotation in JAX-RS won't work with it (at least that's what I've read in their documentation).
So, JAX-RS supports #Injected annotations and in order for it to know where to get bean definitions from, I had to go to the class that defines the ResourceConfig and add the following lines to it:
.register(new AbstractBinder() {
#Override
protected void configure() {
bind(beanImpl1).to(BeanInterface1.class);
bind(beanImpl2).to(BeanInterface2.class);
}
})
The actual beanImpl1 and beanImpl2 bean instances were coming through the constructor of that class, which in turn was instantiated from our Spring through the XML configuration.

Spring boot metrics with Jersey2

I have an application running spring-boot, jersey2 and spring metrics:
below is maven snippet:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Jersey used to work well until introducing actuator dependency.
Then following bean has been created to make Jersey working as filter:
#Bean
public FilterRegistrationBean jerseyFilterRegistration() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setName("jerseyFilter");
bean.setFilter(new ServletContainer(resourceConfig()));
bean.setOrder(Ordered.LOWEST_PRECEDENCE);
bean.addInitParameter("com.sun.jersey.config.property.WebPageContentRegex", managementContextRegex);
return bean;
}
Metrics are mapped to /admin path. With this configuration I cannot make metrics working. However by adding management.port (different than main app port) both Jersey resource and metrics are available.
What I'm missing here to make both metrics and Jersey resource start working on the same port?
"com.sun.jersey.config.property.WebPageContentRegex"
This is the wrong property. That's for Jersey 1.x. For 2.x, it should be
"jersey.config.servlet.filter.staticContentRegex"
See ServletProperties.FILTER_STATIC_CONTENT_REGEX
As an aside you can avoid having to define your own FilterRegistrationBean by simply setting a couple configuration properties. In your application.properties, you could use the following
spring.jersey.type=filter
spring.jersey.init.jersey.config.servlet.filter.staticContentRegex=<your-regex>
Or you can configure the regex in your ResourceConfig subclass
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
property(ServletProperties.FILTER_STATIC_CONTENT_REGEX, "<your-regex>");
}
}
As another side, just an FYI, the cause of the problem is the default /* url-mapping used for Jersey. If you can change it, doing so would solve the problem. For instance /api. You can configure that in the properties with spring.jersey.applicationPath=/api or with #ApplicationPath("/api") on the ResourceConfig subclass.
And the final aside, there is also a property
ServletProperties.FILTER_FORWARD_ON_404
"jersey.config.servlet.filter.forwardOn404"
I'm not exactly sure how the staticContenRegex property works, I never really dug into to source code. So I don't know if it just does some file IO to get the static file or it forwards the request, but if it does some file IO, then I don't think the property will work for your use case, as the endpoints are not files. In which case the forwardOn404 should work.

Resources