Angular CLI with Spring Boot - spring

I have 2 projects. An Angular2 app which I build with Angular-cli and a Spring Boot app which will only serve the Angular2 app. I build the Angular2 app with ng build which generates a dist folder. I then put the content of the dist folder in the Spring Boot app inside src/main/resources/static.
My spring boot app has two files.
The Spring boot application class :
#SpringBootApplication
public class SpringBoot extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringBoot.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBoot.class, args);
}
}
And the application.properties file:
server.contextPath=/
server.port=80
It works well but if I go to an url and hit the refresh button, I get the Whitelabel Error Page. I know it's because the URLs are not serving the index.html when they don't match a resource file.
How can I configure my Spring Boot app to serve index.html if the url doesn't match a resource file?

You are correct that index.html needs to be served back for endpoints unknown to Spring. Then arrange for the Angular app to manage unknown routes.
I handle this situation with a WebMvcConfigurerAdapter. Also put static content file types in here.
Add a config directory and in it add a Java file WebMvcConfig (for example) with this content:
package com.yourdomain.yourapp.config;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.resource.PathResourceResolver;
import java.io.IOException;
import javax.inject.Inject;
#Configuration
#EnableConfigurationProperties({ ResourceProperties.class })
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Inject
private ResourceProperties resourceProperties = new ResourceProperties();
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
Integer cachePeriod = resourceProperties.getCachePeriod();
final String[] staticLocations = resourceProperties.getStaticLocations();
final String[] indexLocations = new String[staticLocations.length];
for (int i = 0; i < staticLocations.length; i++) {
indexLocations[i] = staticLocations[i] + "index.html";
}
registry.addResourceHandler(
"/**/*.css",
"/**/*.html",
"/**/*.js",
"/**/*.json",
"/**/*.bmp",
"/**/*.jpeg",
"/**/*.jpg",
"/**/*.gif",
"/**/*.ico",
"/**/*.png",
"/**/*.ttf",
"/**/*.wav",
"/**/*.mp3",
"/**/*.eot",
"/**/*.svg",
"/**/*.woff",
"/**/*.woff2",
"/**/*.map"
)
.addResourceLocations(staticLocations)
.setCachePeriod(cachePeriod);
registry.addResourceHandler("/**")
.addResourceLocations(indexLocations)
.setCachePeriod(cachePeriod)
.resourceChain(true)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
return location.exists() && location.isReadable() ? location : null;
}
});
}
}
I think you will also have to specify the config package for component scan. Maybe try without first and see if it works.
#SpringBootApplication
#ComponentScan( basePackages = { "com.yourdomain.yourapp.config" })
public class SpringBoot extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringBoot.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBoot.class, args);
}
}
In case your missing dependencies. This is what I have in my build.gradle:
dependencies {
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web'
compile group: 'javax.inject', name: 'javax.inject', version: '1'
optional group: 'org.springframework.boot', name: 'spring-boot-configuration-processor'
providedRuntime group: 'org.springframework.boot', name: 'spring-boot-starter-tomcat'
testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test'
}
Hope this helps :-)

Here you can find my Spring Boot 2 and Angular 6 starter project.
Spring Boot application is created using SPRING INITIALIZR, Angular application is created using Angular CLI.

Related

Spring boot - scan packages

I am starting to write an application in spring boot and below is how my package structure looks:
com.practice.spring.project.helloworld.HelloworldApplication.java
com.practice.spring.project.repository.EmployeeRepository.java
com.practice.spring.project.model.Employee.java
Below is how i had my application startup successfully,
#SpringBootApplication
#ComponentScan(basePackages = "com.practice.spring.project.DB", basePackageClasses = InitDatabase.class)
#EnableJpaRepositories(basePackages = "com.practice.spring.project.repository" , basePackageClasses = EmployeeRepository.class)
public class HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloworldApplication.class, args);
}
#Bean
public CommandLineRunner run(EmployeeRepository employeeRepository) throws Exception {
return (args) -> {
System.out.println("Calling it after the application context is all loaded up");
employeeRepository.save(new Employee("Ashwin", "Architect"));
};
}
}
My question is should I have to specify the base-packages & baseClasses for every class I add ? It would be tough if have 10 packages having 10 different classes.
Am sure there should be an easier way to scan and instantiate classes in different package.
Figured out a way - set the basePackages to com.practice.spring.project.*
#ComponentScan(basePackages = "com.practice.spring.project.*")

How to revert Spring Boot v2.0.1 project back to v1.3.1

I have managed to get my small, simple SpringBoot/Gradle REST service working with Spring Boot v2.0.1. However, for compatibility with other projects in my group, I would like to switch to v1.3.1 [sic]. When I do that, though, I got compilation errors on the following code:
package com.ui.usersetting.UserSettingController;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(UserSettingControllerApplication.class);
}
}
and if I simply comment out that code, my REST service no longer recognizes its endpoints. Is there some way to bridge this, e.g., some v1.3.1 equivalent for SpringBootServeletInitializer?
In SpringBoot 1.3 SpringBootServletInitializer is located in: org.springframework.boot.context.web.SpringBootServletInitializer
update your code like this:
package com.ui.usersetting.UserSettingController;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(UserSettingControllerApplication.class);
}
}

New Functional Web Framework with jetty

I wanted to setup an example for New in Spring 5: Functial Web Framework
So I set up a RouteConfiguration:
#Configuration
public class RouteConfiguration {
#Autowired
private MyService myService;
#Bean
public RouterFunction<?> routerFunction() {
return route(
GET("/first")
, myService::getItemsFirst)
.and(route(
GET("/second")
, myService::getItemsSecond));
}
}
I started my application using jetty and at first it seemed to work... until I wanted to call one of my methods: localhost:8080/first and it returned a 404.
Did I define my route configuration wrong or why arent the routes accessible?
EDIT
With netty you need to provide a Server Configuration Like the following:
#Configuration
public class HttpServerConfiguration {
#Autowired
private Environment environment;
#Bean
public HttpServer httpServer(final RouterFunction<?> routerFunction) {
final HttpHandler httpHandler = RouterFunctions.toHttpHandler(routerFunction);
final ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
final HttpServer server = HttpServer.create("localhost", Integer.valueOf(this.environment.getProperty("server.port")));
server.newHandler(adapter);
return server;
}
}
But I could not find something like this for jetty.
EDIT 2
My Dependencies:
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencyManagement {
dependencies {
dependency (group: 'org.springframework.cloud', name: 'spring-cloud-starter-consul-discovery', version: '2.0.0.M1')
dependencySet (group: 'org.hibernate', version: '5.2.8.Final') {
entry 'hibernate-core'
entry 'hibernate-entitymanager'
entry 'hibernate-spatial'
}
}
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-hateoas')
compile('org.springframework.boot:spring-boot-starter-jetty')
compile('org.springframework.boot:spring-boot-starter-webflux') {
exclude module: 'spring-boot-starter-reactor-netty'
}
compile('org.springframework.boot:spring-boot-starter-actuator')
compile('org.springframework.boot:spring-boot-autoconfigure')
compile('org.springframework.boot:spring-boot-actuator')
compile('org.springframework.cloud:spring-cloud-starter-consul')
compile('org.springframework.cloud:spring-cloud-starter-consul-discovery')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('junit:junit')
}
Spring-Boot Version: 2.0.0.M3
Reading the comments, it seems this was an issue with dependencies bringing spring-boot-starter-web; if it is present, a Spring MVC application is started by Spring Boot.
There's a way to explicitly tell Spring Boot the type of the application, in the main Application class:
public static void main(String[] args) {
SpringApplication application = new SpringApplication(AgentApplication.class);
application.setWebApplicationType(WebApplicationType.REACT‌​IVE);
application.run(args);
}

How to expose Hystrix Stream on Spring Actuator port?

I am using Jetty embedded server in the Spring Boot application.
To handle requests I provide my custom handler like that.
#Slf4j
#Configuration
#EnableWebMvc
#SpringBootApplication
public class Main {
public static void main(String... args) {
new SpringApplicationBuilder().sources(Main.class).run(args);
}
#Bean
public EmbeddedServletContainerCustomizer customizer(JettyRequestHandler myCustomHandler) throws MalformedURLException {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof JettyEmbeddedServletContainerFactory) {
customizeJetty((JettyEmbeddedServletContainerFactory) container);
}
}
private void customizeJetty(JettyEmbeddedServletContainerFactory jetty) {
jetty.addServerCustomizers((JettyServerCustomizer) server -> {
HandlerCollection handlerCollection = new HandlerCollection();
handlerCollection.setHandlers(new Handler[]{myCustomHandler, server.getHandler()});
server.setHandler(handlerCollection);
});
}
};
}
}
I am listening for a requests on a standard 8080 port. I included also Spring Boot Actuator into my project to get some production endpoints (health, etc.). It starts on another port: 8181.
Additionally I am using Hystrix for circuit breaking purposes.
My question is how to enable Hystrix Stream to be exposed on actuator port?
Currently I managed only to expose it on standard port 8080 with following piece of code:
#Bean
public ServletRegistrationBean hystrixStreamServlet(){
return new ServletRegistrationBean(new HystrixMetricsStreamServlet(), "/hystrix.stream");
}
But I would like to expose it on another, to have the default one only for application purposes.
Those are some of my dependecies:
compile 'com.netflix.hystrix:hystrix-core:1.5.3'
compile 'com.netflix.hystrix:hystrix-metrics-event-stream:1.5.3'
compile 'org.springframework.boot:spring-boot-starter-actuator:1.3.5.RELEASE'
I would like NOT to use Spring Cloud where is #EnableHystrix that gives the stream on the actuator port actually.
Actually I did what #m-deinum proposed and it worked. I used Spring Cloud Stack.
To achieve Hystrix Stream on actuator I added dependecies:
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter', version: '1.1.1.RELEASE' // spring cloud starter
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-hystrix', version: '1.1.3.RELEASE' // spring cloud hystrix starter
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-ribbon', version: '1.1.3.RELEASE' // spring ribbon starter
And the annotation on the Main class:
#EnableCircuitBreaker
#SpringBootApplication
public class Main {
public static void main(String... args) {
new SpringApplicationBuilder().sources(Main.class).run(args);
}
// ...
}

How to display "welcome.jsp" in spring boot?

Facing issues while displaying customized welcome.jsp in spring boot application.
It always displays "index.html" while I want to display customized jsp file "welcome.jsp"..
Request help.
1) make sure springmvc options in application.properties
spring.mvc.view.prefix: /WEB-INF/jsp/
spring.mvc.view.suffix: .jsp
2) add src/main/webapp/WEB-INF/jsp/welcome.jsp
3) modify Application like this:
package com.lenicliu.spring.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#SpringBootApplication
public class Application extends WebMvcConfigurerAdapter {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("welcome");
}
}
Please refer to http://docs.spring.io/spring/docs/4.2.6.RELEASE/spring-framework-reference/htmlsingle/#mvc-config-view-controller
Then, run application, and you can find the log:
Root mapping to handler of type [class org.springframework.web.servlet.mvc.ParameterizableViewController]

Resources