So I'm new with all the Spring Framework stuff and I'm working in a project that uses Spring MVC, BUT DOESN'T USE SPRING BOOT. The problem is, I need to implement the internationalization (i18n) with THYMELEAF AND SPRING MVC, WITHOUT BOOT, and all the examples that I have found use Spring Boot too. Is there some way to set up the example below without Spring Boot, only MVC and THYMELEAF? Thanks.
aplication.java
import java.util.Locale;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
#SpringBootApplication
public class MySpringBootApp implements WebMvcConfigurer {
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(Locale.ENGLISH);
return sessionLocaleResolver;
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor result = new LocaleChangeInterceptor();
result.setParamName("lang");
return result;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
public static void main(String[] args) {
SpringApplication.run(MySpringBootApp.class, args);
}
}
controller.java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class MyController {
#RequestMapping("/i18n")
public ModelAndView i18n() {
return new ModelAndView("i18n_page");
}
}
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.github.web</groupId>
<artifactId>spring-mvc-thymeleaf-i18n</artifactId>
<version>1.0.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Related
I'm creating an application with spring boot and I want to authenticate my application using JWT but it is in microservice. In the JWT service it runs normally but I want to authenticate all the microservice services so I put the JWT service code for the api-gateway but because it uses spring-cloud-starter-gateway I can't use spring-boot-starter-web so far everything well. My big problem is that when I run some service, a login page appears. I wanted to remove this page does anyone have any idea how to do this?
Here is my application code below:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>routing</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>routing</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2021.0.4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>3.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties:
server.port=8080
spring.application.name=routing
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
eureka.instance.hostname=localhost
spring.cloud.gateway.discovery.locator.enabled=true
rsa.publickey=classpath:carts/public.pem
rsa.privatekey=classpath:carts/private.pem
#spring.main.web-application-type=reactive
spring.cloud.gateway.enabled=true
spring.cloud.gateway.routes[0].id=user
spring.cloud.gateway.routes[0].uri=lb://USER
spring.cloud.gateway.routes[0].predicates=Path=/user/**
spring.cloud.gateway.routes[1].id=testes
spring.cloud.gateway.routes[1].uri=lb://TESTES
spring.cloud.gateway.routes[1].predicates=Path=/testes/**
spring.cloud.gateway.routes[2].id=user-create
spring.cloud.gateway.routes[2].uri=lb://USER-CREATE
spring.cloud.gateway.routes[2].predicates=Path=/user-create/**
spring.cloud.gateway.routes[3].id=jwt
spring.cloud.gateway.routes[3].uri=lb://JWT
spring.cloud.gateway.routes[3].predicates=Path=/**
RsaKeyPropreties.java:
package com.example.routing.config;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import org.springframework.boot.context.properties.ConfigurationProperties;
#ConfigurationProperties(prefix = "rsa")
public record RsaKeyPropreties(RSAPublicKey publickey,RSAPrivateKey privatekey) {
}
SecurityConfig.java:
package com.example.routing.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import com.nimbusds.jose.proc.SecurityContext;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import static org.springframework.security.config.Customizer.withDefaults;
import org.springframework.boot.autoconfigure.AutoConfiguration;
#Configuration
#AutoConfiguration
#EnableWebSecurity
public class SecurityConfig {
private final RsaKeyPropreties Rsakeys;
public SecurityConfig(RsaKeyPropreties Rsakeys) {
this.Rsakeys = Rsakeys;
}
#Bean
public InMemoryUserDetailsManager user(){
return new InMemoryUserDetailsManager(
User.withUsername("username")
.password("{noop}password")
.authorities("read")
.build()
);
}
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
return http
.csrf(csrf->csrf.disable())
//.authorizeRequests(auth->auth.antMatchers("/user**").authenticated())
.authorizeRequests(auth->auth.anyRequest().authenticated())
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.sessionManagement(session->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.httpBasic(withDefaults()).build();
}
#Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withPublicKey(Rsakeys.publickey()).build();
}
#Bean
JwtEncoder jwtEncoder() {
JWK jwk = new RSAKey.Builder(Rsakeys.publickey()).privateKey(Rsakeys.privatekey()).build();
JWKSource<SecurityContext> jws = new ImmutableJWKSet<>(new JWKSet(jwk));
return new NimbusJwtEncoder(jws);
}
}
RoutingApplication.java:
package com.example.routing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import com.example.routing.config.RsaKeyPropreties;
#EnableConfigurationProperties(RsaKeyPropreties.class)
#SpringBootApplication
#EnableDiscoveryClient
public class RoutingApplication {
public static void main(String[] args) {
SpringApplication.run(RoutingApplication.class, args);
}
}
Thanks!
For Servlet app, this will return 401 (unauthorized) instead of 302 (redirect to login) when authorization is missing or invalid:
http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
});
For a reactive app, you can provide a ServerAccessDeniedHandler bean instead:
#Bean
ServerAccessDeniedHandler serverAccessDeniedHandler() {
return (var exchange, var ex) -> exchange.getPrincipal().flatMap(principal -> {
final var response = exchange.getResponse();
response.setStatusCode(principal instanceof AnonymousAuthenticationToken ? HttpStatus.UNAUTHORIZED : HttpStatus.FORBIDDEN);
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
final var dataBufferFactory = response.bufferFactory();
final var buffer = dataBufferFactory.wrap(ex.getMessage().getBytes(Charset.defaultCharset()));
return response.writeWith(Mono.just(buffer)).doOnError(error -> DataBufferUtils.release(buffer));
});
}
Spring-boot starters I maintain here (which are thin wrappers arround spring-boot-starter-oauth2-resource-server) are doing that by default plus a few other usefull things:
map authorities from a list of claims of your choice (giving you hand on case and prefix)
stateless session-management (like you do)
disabled CSRF (only if session-management is left stateless)
fine grained CORS config from properties
multi-tenancy (accept more than just one JWT issuer)
As a side note, what about having your gateway being a pass-through for OAuth2 (just forward requests authorization header and responses HTTP status) and implement resources access-control (spring-security .authorizeRequests() and #PreAuthorize rules) on resource-server, where you can unit-test it?
I am trying to implement Bucket4J rate limiter in Netflix Zuul Api Gateway. I have added Interceptor for Rate Limiting the requests using WebMvcConfigurer.
package com.rajkumar.apiigateway;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.rajkumar.apiigateway.ratelimit.interceptor.RateLimitInterceptor;
#SpringBootApplication
#EnableZuulProxy
public class ApiiGatewayApplication implements WebMvcConfigurer{
#Autowired
#Lazy
RateLimitInterceptor rateLimitInterceptor;
public static void main(String[] args) {
new SpringApplicationBuilder(ApiiGatewayApplication.class)
.run(args);
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(rateLimitInterceptor)
.addPathPatterns("/api/service_1/throttling/users");
}
}
And Interceptor for rate limiting looks like
package com.rajkumar.apiigateway.ratelimit.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import com.rajkumar.apiigateway.ratelimit.service.RateLimitService;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.ConsumptionProbe;
#Component
public class RateLimitInterceptor implements HandlerInterceptor {
private static final String HEADER_API_KEY = "X-API-KEY";
private static final String HEADER_LIMIT_REMAINING = "X-RATE-LIMIT-REMAINING";
private static final String HEADER_RETRY_AFTER = "X-RATE-LIMIT-RETRY-AFTER-SECONDS";
#Autowired
RateLimitService rateLimitService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String apiKey = request.getHeader(HEADER_API_KEY);
if(apiKey == null || apiKey.isEmpty()) {
response.sendError(HttpStatus.OK.value(), HEADER_API_KEY+" request header is mandatory");
return false;
}
Bucket tokenBucket = rateLimitService.resolveBucket(request.getHeader(HEADER_API_KEY));
ConsumptionProbe probe = tokenBucket.tryConsumeAndReturnRemaining(1);
if(probe.isConsumed()) {
response.addHeader(HEADER_LIMIT_REMAINING, Long.toString(probe.getRemainingTokens()));
return true;
}
response.addHeader(HEADER_RETRY_AFTER, Long.toString(probe.getNanosToWaitForRefill()/1000000000));
response.sendError(HttpStatus.TOO_MANY_REQUESTS.value(),"You have exceeded your request limit");
return false;
}
}
and other dependent component
package com.rajkumar.apiigateway.ratelimit.service;
import java.util.concurrent.TimeUnit;
import org.springframework.stereotype.Component;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.rajkumar.apiigateway.ratelimit.RateLimit;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bucket4j;
#Component
public class RateLimitService {
private LoadingCache<String, Bucket> cache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.build(this::newBucket);
public Bucket resolveBucket(String apiKey) {
return cache.get(apiKey);
}
private Bucket newBucket(String apiKey) {
RateLimit plan = RateLimit.resolvePlanFromApiKey(apiKey);
Bucket bucket = Bucket4j.builder()
.addLimit(plan.getLimit())
.build();
return bucket;
}
}
package com.rajkumar.apiigateway.ratelimit;
import java.time.Duration;
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Refill;
public enum RateLimit {
FREE(2L),
BASIC(4L),
PROFESSIONAL(10L);
private Long tokens;
private RateLimit(Long tokens) {
this.tokens = tokens;
}
public static RateLimit resolvePlanFromApiKey(String apiKey) {
if(apiKey==null || apiKey.isEmpty()) {
return FREE;
}
else if(apiKey.startsWith("BAS-")) {
return BASIC;
}
else if(apiKey.startsWith("PRO-")) {
return PROFESSIONAL;
}
return FREE;
}
public Bandwidth getLimit() {
return Bandwidth.classic(tokens, Refill.intervally(tokens, Duration.ofMinutes(1)));
}
}
and pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.rajkumar</groupId>
<artifactId>apii-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>apii-gateway</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<!-- <version>2.5.5</version> -->
</dependency>
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>4.10.0</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
and application.properties
server.port = 8080
spring.application.name = api-gateway
#routing for service 1
zuul.routes.service_1.path = /api/service_1/**
zuul.routes.service_1.url = http://localhost:8081/
#routing for service 2
zuul.routes.service_2.path = /api/service_2/**
zuul.routes.service_2.url = http://localhost:8082/
When I am trying to hit api gateway (http://localhost:8080/api/service_1/throttling/users): it is not passing through the interceptor. Any help is appreciated.
Thanks in advance.
I mapped the controller to "/home" that returns "home" which is in WEB-INF folder and I made the prefix to "/WEB-INF/" but I get the same error repeatedly and I using spring with maven
home.jsp is inside the /WEB-INF/
and the controller return home when we made "/home" request
and the prefix and suffix are correctly mapped
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>security-spring</groupId>
<artifactId>007-spring-security</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>007-spring-security Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<!-- Spring MVC support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!-- Servlet, JSP and JSTL support -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>007-spring-security</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
AppConfig.java
package com.demo.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="com.demo.security")
public class AppConfig {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
DispatcherServletInitializer.java
package com.demo.security.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
#Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {AppConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[] { "/*" };
}
}
HomeController.java
package com.demo.security.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class HomeController {
#GetMapping("/home")
public String home() {
return "home";
}
}
Whenever I was trying with your code I was getting org.springframework.web.servlet.PageNotFound.noHandlerFound No mapping found for HTTP request on my tomcat console and getting 404 on the browser. By changing URL pattern from /* to / solved the problem.
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
I've some google in the search field about my problem, but i don't get solution. In the spring project (not boot) creating in intellij idea via maven I set up all things (see below) like tomcat configuration, java config for simple helloWorld etc... But: i got result only with exploded war and not in the welcome directory: i saw it at localhost:8080 (not at localhost:8080/welcome).
if i set artifact as war I get usual tomcat home page and nothing about my project.
code snpippets below:
Controller:
package com.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class HomeController {
#RequestMapping("/")
public String welcome(Model model){
model.addAttribute("hello", "Hello");
model.addAttribute("anyone", "anyone");
return "welcome";
}
}
WebApplicationContextConfig:
package com.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
#Configuration
#EnableWebMvc
#ComponentScan("com")
public class WebAppContextConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public InternalResourceViewResolver getInternalViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setViewClass(JstlView.class);
resolver.setPrefix("/pages/");
resolver.setSuffix(".jsp");
return resolver;
}
}
DispatcherServletInitializer:
package com.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebAppContextConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.github</groupId>
<artifactId>webstore</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.8.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Run configuration window:
Folder structure
I would suggest you not to bother yourself with Spring Configuration files and use Spring Boot where you have it already configured for you.
For instance, this 'hello world' sample should do what you want: https://github.com/pgrimard/spring-boot-hello-world
Also, consider http://start.spring.io/ for generating a skeleton of your project.
I'm following Spring in Action 4th Edition Chapter 5, but I'm stuck at the very first example.
Here is my Eclipse Luna project structure:
If I run this project as Spring Boot App, then it throws exceptions:
org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:133)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:474)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:957)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:946)
at spittr.SpittrApplication.main(SpittrApplication.java:10)
Caused by: org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getEmbeddedServletContainerFactory(EmbeddedWebApplicationContext.java:183)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:156)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:130)
... 7 common frames omitted
How can I fix this problem?
Contents of all files:
SpittrApplication.java:
package spittr;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpittrApplication {
public static void main(String[] args) {
SpringApplication.run(SpittrApplication.class, args);
}
}
SpittrWebAppInitializer.java:
package spittr.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class SpittrWebAppInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
}
WebConfig.java:
package spittr.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableWebMvc
#ComponentScan("spittr.web")
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
RootConfig.java:
package spittr.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
#Configuration
#ComponentScan(basePackages = { "spitter" }, excludeFilters = { #Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class) })
public class RootConfig {
}
HomeController.java:
package spittr.web;
import static org.springframework.web.bind.annotation.RequestMethod.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class HomeController {
#RequestMapping(value = "/", method = GET)
public String home() {
return "home";
}
}
home.jsp:
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%# page session="false" %>
<html>
<head>
<title>Spittr</title>
<link rel="stylesheet"
type="text/css"
href="<c:url value="/resources/style.css" />" >
</head>
<body>
<h1>Welcome to Spittr</h1>
Spittles |
Register
</body>
</html>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.spittr</groupId>
<artifactId>spittr</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spittr</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
When using Spring Boot you should not include the other Spring dependencies directly, but rely on Boot's own dependency management. When using the provided "starters" you can be sure that all needed libraries will be included in a matching version.
Instead of including spring-mvc artifact in your pom.xml:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
use the dedicated Boot starter for webapps, spring-boot-starter-web:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
More details in the the official guide
I believe other parts of your pom.xml are superfluous as well (spring-boot-starter, javax.servlet-api, spring-boot-maven-plugin)
If You are using another application which listens to the port that is the same as Tomcat listens, this may also be the problem.