/01-hello-world-rest-api/Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
EXPOSE 80
ADD target/*.jar app.jar
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
/01-hello-world-rest-api/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.in28minutes.rest.webservices</groupId >
<artifactId >01-hello-world-rest-api</artifactId >
<version >0.0.1-SNAPSHOT</version >
<packaging >jar</packaging >
<description >Demo project for Spring Boot</description >
<name >hello-world-rest-api</name >
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-parent</artifactId >
<version >2.1.7.RELEASE</version >
<relativePath /> <!-- lookup parent from repository -->
</parent >
<properties >
<project .build.sourceEncoding>UTF-8</project .build.sourceEncoding>
<project .reporting.outputEncoding>UTF-8</project .reporting.outputEncoding>
<java .version>1.8</java .version>
<maven-jar-plugin .version>3.1.1</maven-jar-plugin .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-test</artifactId >
<scope >test</scope >
</dependency >
</dependencies >
<build >
<finalName >hello-world-rest-api</finalName >
<plugins >
<plugin >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-maven-plugin</artifactId >
</plugin >
</plugins >
</build >
<repositories >
<repository >
<id >spring-snapshots</id >
<name >Spring Snapshots</name >
<url >https://repo.spring.io/snapshot</url >
<snapshots >
<enabled >true</enabled >
</snapshots >
</repository >
<repository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestone</url >
<snapshots >
<enabled >false</enabled >
</snapshots >
</repository >
</repositories >
<pluginRepositories >
<pluginRepository >
<id >spring-snapshots</id >
<name >Spring Snapshots</name >
<url >https://repo.spring.io/snapshot</url >
<snapshots >
<enabled >true</enabled >
</snapshots >
</pluginRepository >
<pluginRepository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestone</url >
<snapshots >
<enabled >false</enabled >
</snapshots >
</pluginRepository >
</pluginRepositories >
</project >
/01-hello-world-rest-api/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/HelloWorldBean.java
package com .in28minutes .rest .webservices .restfulwebservices ;
public class HelloWorldBean {
private String message ;
public HelloWorldBean (String message ) {
this .message = message ;
}
public String getMessage () {
return message ;
}
public void setMessage (String message ) {
this .message = message ;
}
@ Override
public String toString () {
return String .format ("HelloWorldBean [message=%s]" , message );
}
}
/01-hello-world-rest-api/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/HelloWorldController.java
package com .in28minutes .rest .webservices .restfulwebservices ;
import org .springframework .web .bind .annotation .GetMapping ;
import org .springframework .web .bind .annotation .PathVariable ;
import org .springframework .web .bind .annotation .RestController ;
@ RestController
public class HelloWorldController {
@ GetMapping (path = "/hello-world" )
public String helloWorld () {
return "Hello World - V6" ;
}
@ GetMapping (path = "/hello-world-bean" )
public HelloWorldBean helloWorldBean () {
// throw new RuntimeException("Some Error has Happened! Contact Support at
// ***-***");
return new HelloWorldBean ("Hello World" );
}
/// hello-world/path-variable/in28minutes
@ GetMapping (path = "/hello-world/path-variable/{name}" )
public HelloWorldBean helloWorldPathVariable (@ PathVariable String name ) {
return new HelloWorldBean (String .format ("Hello World, %s" , name ));
}
}
/01-hello-world-rest-api/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplication.java
package com .in28minutes .rest .webservices .restfulwebservices ;
import org .springframework .boot .SpringApplication ;
import org .springframework .boot .autoconfigure .SpringBootApplication ;
@ SpringBootApplication
public class RestfulWebServicesApplication {
public static void main (String [] args ) {
SpringApplication .run (RestfulWebServicesApplication .class , args );
}
}
/01-hello-world-rest-api/src/main/resources/application.properties
logging.level.org.springframework = debug
/01-hello-world-rest-api/src/test/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplicationTests.java
package com .in28minutes .rest .webservices .restfulwebservices ;
import org .junit .Test ;
import org .junit .runner .RunWith ;
import org .springframework .boot .test .context .SpringBootTest ;
import org .springframework .test .context .junit4 .SpringRunner ;
@ RunWith (SpringRunner .class )
@ SpringBootTest
public class RestfulWebServicesApplicationTests {
@ Test
public void contextLoads () {
}
}
/02-todo-web-application-h2/Dockerfile
From tomcat:8.0.51-jre8-alpine
RUN rm -rf /usr/local/tomcat/webapps/*
COPY ./target/*.war /usr/local/tomcat/webapps/ROOT.war
CMD ["catalina.sh","run"]
/02-todo-web-application-h2/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.in28minutes.springboot.web</groupId >
<artifactId >02-todo-web-application-h2</artifactId >
<version >0.0.1-SNAPSHOT</version >
<packaging >war</packaging >
<name >todo-web-application-h2</name >
<description >Demo project for Spring Boot</description >
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-parent</artifactId >
<version >2.1.7.RELEASE</version >
<relativePath /> <!-- lookup parent from repository -->
</parent >
<properties >
<project .build.sourceEncoding>UTF-8</project .build.sourceEncoding>
<project .reporting.outputEncoding>UTF-8</project .reporting.outputEncoding>
<java .version>1.8</java .version>
<maven-jar-plugin .version>3.1.1</maven-jar-plugin .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-data-jpa</artifactId >
</dependency >
<dependency >
<groupId >com.h2database</groupId >
<artifactId >h2</artifactId >
<scope >runtime</scope >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-security</artifactId >
</dependency >
<dependency >
<groupId >javax.servlet</groupId >
<artifactId >jstl</artifactId >
</dependency >
<dependency >
<groupId >org.webjars</groupId >
<artifactId >bootstrap</artifactId >
<version >3.3.6</version >
</dependency >
<dependency >
<groupId >org.webjars</groupId >
<artifactId >bootstrap-datepicker</artifactId >
<version >1.0.1</version >
</dependency >
<dependency >
<groupId >org.webjars</groupId >
<artifactId >jquery</artifactId >
<version >1.9.1</version >
</dependency >
<dependency >
<groupId >org.apache.tomcat.embed</groupId >
<artifactId >tomcat-embed-jasper</artifactId >
<scope >provided</scope >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-tomcat</artifactId >
<scope >provided</scope >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-devtools</artifactId >
<scope >runtime</scope >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-test</artifactId >
<scope >test</scope >
</dependency >
</dependencies >
<build >
<finalName >todo-web-application-h2</finalName >
<plugins >
<plugin >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-maven-plugin</artifactId >
</plugin >
</plugins >
</build >
<repositories >
<repository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestones</url >
</repository >
</repositories >
<pluginRepositories >
<pluginRepository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestones</url >
</pluginRepository >
</pluginRepositories >
</project >
/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.java
package com .in28minutes .springboot .web ;
import java .util .Arrays ;
import java .util .stream .StreamSupport ;
import org .slf4j .Logger ;
import org .slf4j .LoggerFactory ;
import org .springframework .context .event .ContextRefreshedEvent ;
import org .springframework .context .event .EventListener ;
import org .springframework .core .env .AbstractEnvironment ;
import org .springframework .core .env .EnumerablePropertySource ;
import org .springframework .core .env .Environment ;
import org .springframework .core .env .MutablePropertySources ;
import org .springframework .stereotype .Component ;
@ Component
public class EnvironmentConfigurationLogger {
private static final Logger LOGGER = LoggerFactory .getLogger (EnvironmentConfigurationLogger .class );
@ SuppressWarnings ("rawtypes" )
@ EventListener
public void handleContextRefresh (ContextRefreshedEvent event ) {
final Environment environment = event .getApplicationContext ().getEnvironment ();
LOGGER .info ("====== Environment and configuration ======" );
LOGGER .info ("Active profiles: {}" , Arrays .toString (environment .getActiveProfiles ()));
final MutablePropertySources sources = ((AbstractEnvironment ) environment ).getPropertySources ();
StreamSupport .stream (sources .spliterator (), false ).filter (ps -> ps instanceof EnumerablePropertySource )
.map (ps -> ((EnumerablePropertySource ) ps ).getPropertyNames ()).flatMap (Arrays ::stream ).distinct ()
.forEach (prop -> LOGGER .info ("{}" , prop ));// environment.getProperty(prop)
LOGGER .info ("===========================================" );
}
}
/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java
package com .in28minutes .springboot .web ;
import org .springframework .boot .SpringApplication ;
import org .springframework .boot .autoconfigure .SpringBootApplication ;
import org .springframework .boot .builder .SpringApplicationBuilder ;
import org .springframework .boot .web .servlet .support .SpringBootServletInitializer ;
import org .springframework .context .annotation .ComponentScan ;
@ SpringBootApplication
@ ComponentScan ("com.in28minutes.springboot.web" )
public class SpringBootFirstWebApplication extends SpringBootServletInitializer {
@ Override
protected SpringApplicationBuilder configure (SpringApplicationBuilder application ) {
return application .sources (SpringBootFirstWebApplication .class );
}
public static void main (String [] args ) {
SpringApplication .run (SpringBootFirstWebApplication .class , args );
}
}
/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/ErrorController.java
package com .in28minutes .springboot .web .controller ;
import javax .servlet .http .HttpServletRequest ;
import org .springframework .stereotype .Controller ;
import org .springframework .web .bind .annotation .ExceptionHandler ;
import org .springframework .web .servlet .ModelAndView ;
@ Controller ("error" )
public class ErrorController {
@ ExceptionHandler (Exception .class )
public ModelAndView handleException
(HttpServletRequest request , Exception ex ){
ModelAndView mv = new ModelAndView ();
mv .addObject ("exception" , ex .getLocalizedMessage ());
mv .addObject ("url" , request .getRequestURL ());
mv .setViewName ("error" );
return mv ;
}
}
/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java
package com .in28minutes .springboot .web .controller ;
import javax .servlet .http .HttpServletRequest ;
import javax .servlet .http .HttpServletResponse ;
import org .springframework .security .core .Authentication ;
import org .springframework .security .core .context .SecurityContextHolder ;
import org .springframework .security .web .authentication .logout .SecurityContextLogoutHandler ;
import org .springframework .stereotype .Controller ;
import org .springframework .web .bind .annotation .RequestMapping ;
import org .springframework .web .bind .annotation .RequestMethod ;
@ Controller
public class LogoutController {
@ RequestMapping (value = "/logout" , method = RequestMethod .GET )
public String logout (HttpServletRequest request ,
HttpServletResponse response ) {
Authentication authentication = SecurityContextHolder .getContext ()
.getAuthentication ();
if (authentication != null ) {
new SecurityContextLogoutHandler ().logout (request , response ,
authentication );
}
return "redirect:/" ;
}
}
/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/TodoController.java
package com .in28minutes .springboot .web .controller ;
import java .text .SimpleDateFormat ;
import java .util .Date ;
import javax .validation .Valid ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .beans .propertyeditors .CustomDateEditor ;
import org .springframework .security .core .context .SecurityContextHolder ;
import org .springframework .security .core .userdetails .UserDetails ;
import org .springframework .stereotype .Controller ;
import org .springframework .ui .ModelMap ;
import org .springframework .validation .BindingResult ;
import org .springframework .web .bind .WebDataBinder ;
import org .springframework .web .bind .annotation .InitBinder ;
import org .springframework .web .bind .annotation .RequestMapping ;
import org .springframework .web .bind .annotation .RequestMethod ;
import org .springframework .web .bind .annotation .RequestParam ;
import com .in28minutes .springboot .web .model .Todo ;
import com .in28minutes .springboot .web .service .TodoRepository ;
@ Controller
public class TodoController {
@ Autowired
TodoRepository repository ;
@ InitBinder
public void initBinder (WebDataBinder binder ) {
// Date - dd/MM/yyyy
SimpleDateFormat dateFormat = new SimpleDateFormat ("dd/MM/yyyy" );
binder .registerCustomEditor (Date .class , new CustomDateEditor (
dateFormat , false ));
}
@ RequestMapping (value = "/list-todos" , method = RequestMethod .GET )
public String showTodos (ModelMap model ) {
String name = getLoggedInUserName (model );
model .put ("todos" , repository .findByUser (name ));
//model.put("todos", service.retrieveTodos(name));
return "list-todos" ;
}
private String getLoggedInUserName (ModelMap model ) {
Object principal = SecurityContextHolder .getContext ()
.getAuthentication ().getPrincipal ();
if (principal instanceof UserDetails ) {
return ((UserDetails ) principal ).getUsername ();
}
return principal .toString ();
}
@ RequestMapping (value = "/add-todo" , method = RequestMethod .GET )
public String showAddTodoPage (ModelMap model ) {
model .addAttribute ("todo" , new Todo (0 , getLoggedInUserName (model ),
"Default Desc" , new Date (), false ));
return "todo" ;
}
@ RequestMapping (value = "/delete-todo" , method = RequestMethod .GET )
public String deleteTodo (@ RequestParam int id ) {
//if(id==1)
//throw new RuntimeException("Something went wrong");
repository .deleteById (id );
//service.deleteTodo(id);
return "redirect:/list-todos" ;
}
@ RequestMapping (value = "/update-todo" , method = RequestMethod .GET )
public String showUpdateTodoPage (@ RequestParam int id , ModelMap model ) {
Todo todo = repository .findById (id ).get ();
//Todo todo = service.retrieveTodo(id);
model .put ("todo" , todo );
return "todo" ;
}
@ RequestMapping (value = "/update-todo" , method = RequestMethod .POST )
public String updateTodo (ModelMap model , @ Valid Todo todo ,
BindingResult result ) {
if (result .hasErrors ()) {
return "todo" ;
}
todo .setUser (getLoggedInUserName (model ));
repository .save (todo );
//service.updateTodo(todo);
return "redirect:/list-todos" ;
}
@ RequestMapping (value = "/add-todo" , method = RequestMethod .POST )
public String addTodo (ModelMap model , @ Valid Todo todo , BindingResult result ) {
if (result .hasErrors ()) {
return "todo" ;
}
todo .setUser (getLoggedInUserName (model ));
repository .save (todo );
/*service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(),
false);*/
return "redirect:/list-todos" ;
}
}
/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java
package com .in28minutes .springboot .web .controller ;
import org .springframework .security .core .context .SecurityContextHolder ;
import org .springframework .security .core .userdetails .UserDetails ;
import org .springframework .stereotype .Controller ;
import org .springframework .ui .ModelMap ;
import org .springframework .web .bind .annotation .RequestMapping ;
import org .springframework .web .bind .annotation .RequestMethod ;
@ Controller
public class WelcomeController {
@ RequestMapping (value = "/" , method = RequestMethod .GET )
public String showWelcomePage (ModelMap model ) {
model .put ("name" , getLoggedinUserName ());
return "welcome" ;
}
private String getLoggedinUserName () {
Object principal = SecurityContextHolder .getContext ()
.getAuthentication ().getPrincipal ();
if (principal instanceof UserDetails ) {
return ((UserDetails ) principal ).getUsername ();
}
return principal .toString ();
}
}
/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/model/Todo.java
package com .in28minutes .springboot .web .model ;
import java .util .Date ;
import javax .persistence .Entity ;
import javax .persistence .GeneratedValue ;
import javax .persistence .Id ;
import javax .validation .constraints .Size ;
@ Entity
public class Todo {
@ Id
@ GeneratedValue
private int id ;
private String user ;
@ Size (min =10 , message ="Enter at least 10 Characters..." )
private String desc ;
private Date targetDate ;
private boolean isDone ;
public Todo () {
super ();
}
public Todo (int id , String user , String desc , Date targetDate ,
boolean isDone ) {
super ();
this .id = id ;
this .user = user ;
this .desc = desc ;
this .targetDate = targetDate ;
this .isDone = isDone ;
}
public int getId () {
return id ;
}
public void setId (int id ) {
this .id = id ;
}
public String getUser () {
return user ;
}
public void setUser (String user ) {
this .user = user ;
}
public String getDesc () {
return desc ;
}
public void setDesc (String desc ) {
this .desc = desc ;
}
public Date getTargetDate () {
return targetDate ;
}
public void setTargetDate (Date targetDate ) {
this .targetDate = targetDate ;
}
public boolean isDone () {
return isDone ;
}
public void setDone (boolean isDone ) {
this .isDone = isDone ;
}
@ Override
public int hashCode () {
final int prime = 31 ;
int result = 1 ;
result = prime * result + id ;
return result ;
}
@ Override
public boolean equals (Object obj ) {
if (this == obj ) {
return true ;
}
if (obj == null ) {
return false ;
}
if (getClass () != obj .getClass ()) {
return false ;
}
Todo other = (Todo ) obj ;
if (id != other .id ) {
return false ;
}
return true ;
}
@ Override
public String toString () {
return String .format (
"Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]" , id ,
user , desc , targetDate , isDone );
}
}
/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java
package com .in28minutes .springboot .web .security ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .context .annotation .Configuration ;
import org .springframework .security .config .annotation .authentication .builders .AuthenticationManagerBuilder ;
import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
import org .springframework .security .config .annotation .web .configuration .WebSecurityConfigurerAdapter ;
@ Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
//Create User - in28Minutes/dummy
@ Autowired
public void configureGlobalSecurity (AuthenticationManagerBuilder auth )
throws Exception {
auth .inMemoryAuthentication ().withUser ("in28minutes" ).password ("{noop}dummy" )
.roles ("USER" , "ADMIN" );
}
@ Override
protected void configure (HttpSecurity http ) throws Exception {
http .authorizeRequests ().antMatchers ("/login" , "/h2-console/**" ).permitAll ()
.antMatchers ("/" , "/*todo*/**" ).access ("hasRole('USER')" ).and ()
.formLogin ();
http .csrf ().disable ();
http .headers ().frameOptions ().disable ();
}
}
/02-todo-web-application-h2/src/main/java/com/in28minutes/springboot/web/service/TodoRepository.java
package com .in28minutes .springboot .web .service ;
import java .util .List ;
import org .springframework .data .jpa .repository .JpaRepository ;
import com .in28minutes .springboot .web .model .Todo ;
public interface TodoRepository extends JpaRepository <Todo , Integer > {
List <Todo > findByUser (String user );
}
/02-todo-web-application-h2/src/main/resources/application.properties
spring.mvc.view.prefix =/WEB-INF/jsp/
spring.mvc.view.suffix =.jsp
logging.level.org.springframework.web =INFO
spring.jpa.show-sql =true
spring.h2.console.enabled =true
spring.h2.console.settings.web-allow-others =true
/02-todo-web-application-h2/src/main/resources/data.sql
insert into TODO
values(10001, 'Learn Spring Boot', false, sysdate(), 'in28minutes');
insert into TODO
values(10002, 'Learn Angular JS', false, sysdate(), 'in28minutes');
insert into TODO
values(10003, 'Learn to Dance', false, sysdate(), 'in28minutes');
/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/common/footer.jspf
<script src="webjars/jquery/1.9.1/jquery.min.js"></script>
<script src="webjars/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script
src="webjars/bootstrap-datepicker/1.0.1/js/bootstrap-datepicker.js"></script>
<script>
$('#targetDate').datepicker({
format : 'dd/mm/yyyy'
});
</script>
</body>
</html>
/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/common/header.jspf
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<html>
<head>
<title>First Web Application</title>
<link href="webjars/bootstrap/3.3.6/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body>
/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/common/navigation.jspf
<nav role="navigation" class="navbar navbar-default">
<div class="">
<a href="http://www.in28minutes.com" class="navbar-brand">in28Minutes</a>
</div>
<div class="navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="./">Home</a></li>
<li><a href="./list-todos">Todos</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="./logout">Logout</a></li>
</ul>
</div>
</nav>
/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/error.jsp
<%@ include file="common/header.jspf"%>
<%@ include file="common/navigation.jspf"%>
<div class="container">
An exception occurred! Please contact Support!
</div>
<%@ include file="common/footer.jspf"%>
/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/list-todos.jsp
<%@ include file="common/header.jspf" %>
<%@ include file="common/navigation.jspf" %>
<div class="container">
<table class="table table-striped">
<caption>Your todos are</caption>
<thead>
<tr>
<th>Description</th>
<th>Target Date</th>
<th>Is it Done?</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<c:forEach items="${todos}" var="todo">
<tr>
<td>${todo.desc}</td>
<td><fmt:formatDate value="${todo.targetDate}" pattern="dd/MM/yyyy"/></td>
<td>${todo.done}</td>
<td><a type="button" class="btn btn-success"
href="./update-todo?id=${todo.id}">Update</a></td>
<td><a type="button" class="btn btn-warning"
href="./delete-todo?id=${todo.id}">Delete</a></td>
</tr>
</c:forEach>
</tbody>
</table>
<div>
<a class="button" href="./add-todo">Add a Todo</a>
</div>
</div>
<%@ include file="common/footer.jspf" %>
/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/todo.jsp
<%@ include file="common/header.jspf" %>
<%@ include file="common/navigation.jspf" %>
<div class="container">
<form:form method="post" modelAttribute="todo">
<form:hidden path="id" />
<fieldset class="form-group">
<form:label path="desc">Description</form:label>
<form:input path="desc" type="text" class="form-control"
required="required" />
<form:errors path="desc" cssClass="text-warning" />
</fieldset>
<fieldset class="form-group">
<form:label path="targetDate">Target Date</form:label>
<form:input path="targetDate" type="text" class="form-control"
required="required" />
<form:errors path="targetDate" cssClass="text-warning" />
</fieldset>
<button type="submit" class="btn btn-success">Add</button>
</form:form>
</div>
<%@ include file="common/footer.jspf" %>
/02-todo-web-application-h2/src/main/webapp/WEB-INF/jsp/welcome.jsp
<%@ include file="common/header.jspf"%>
<%@ include file="common/navigation.jspf"%>
<div class="container">
Welcome ${name}!! <a href="./list-todos">Click here</a> to manage your
todo's.
</div>
<%@ include file="common/footer.jspf"%>
/02-todo-web-application-h2/src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java
package com .in28minutes .springboot .web ;
import org .junit .Test ;
import org .junit .runner .RunWith ;
import org .springframework .boot .test .context .SpringBootTest ;
import org .springframework .test .context .junit4 .SpringRunner ;
@ RunWith (SpringRunner .class )
@ SpringBootTest
public class SpringBootFirstWebApplicationTests {
@ Test
public void contextLoads () {
}
}
/03-todo-web-application-mysql/Dockerfile
From tomcat:8.0.51-jre8-alpine
RUN rm -rf /usr/local/tomcat/webapps/*
COPY ./target/*.war /usr/local/tomcat/webapps/ROOT.war
CMD ["catalina.sh","run"]
/03-todo-web-application-mysql/docker-compose.yml
version: '3.7'
# Removed subprocess.CalledProcessError: Command '['/usr/local/bin/docker-credential-desktop', 'get']' returned non-zero exit status 1
# I had this:
# cat ~/.docker/config.json
# {"auths":{},"credsStore":"", "credsStore":"desktop","stackOrchestrator":"swarm"}
# I updated to this:
# {"auths":{},"credsStore":"","stackOrchestrator":"swarm"}
services:
todo-web-application:
#image: in28min/todo-web-application-mysql:0.0.1-SNAPSHOT
build:
context: .
#dockerfile: Dockerfile
ports:
- "8080:8080"
restart: always
depends_on: # Start the depends_on first
- mysql
environment:
RDS_HOSTNAME: mysql
RDS_PORT: 3306
RDS_DB_NAME: todos
RDS_USERNAME: todos-user
RDS_PASSWORD: dummytodos
networks:
- todo-web-application-network
# Database Service (Mysql)
mysql:
image: mysql:5.7
ports:
- "3306:3306"
restart: always
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_PASSWORD: dummypassword
MYSQL_USER: todos-user
MYSQL_PASSWORD: dummytodos
MYSQL_DATABASE: todos
volumes:
- mysql-database-data:/var/lib/mysql
networks:
- todo-web-application-network
# Volumes
volumes:
mysql-database-data:
# Networks to be created to facilitate communication between containers
networks:
todo-web-application-network:
/03-todo-web-application-mysql/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.in28minutes.springboot.web</groupId >
<artifactId >03-todo-web-application-mysql</artifactId >
<version >0.0.1-SNAPSHOT</version >
<packaging >war</packaging >
<name >todo-web-application-mysql</name >
<description >Demo project for Spring Boot</description >
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-parent</artifactId >
<version >2.1.7.RELEASE</version >
<relativePath /> <!-- lookup parent from repository -->
</parent >
<properties >
<project .build.sourceEncoding>UTF-8</project .build.sourceEncoding>
<project .reporting.outputEncoding>UTF-8</project .reporting.outputEncoding>
<java .version>1.8</java .version>
<maven-jar-plugin .version>3.1.1</maven-jar-plugin .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-actuator</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-data-jpa</artifactId >
</dependency >
<dependency >
<groupId >com.h2database</groupId >
<artifactId >h2</artifactId >
<scope >test</scope >
</dependency >
<dependency >
<groupId >mysql</groupId >
<artifactId >mysql-connector-java</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-security</artifactId >
</dependency >
<dependency >
<groupId >javax.servlet</groupId >
<artifactId >jstl</artifactId >
</dependency >
<dependency >
<groupId >org.webjars</groupId >
<artifactId >bootstrap</artifactId >
<version >3.3.6</version >
</dependency >
<dependency >
<groupId >org.webjars</groupId >
<artifactId >bootstrap-datepicker</artifactId >
<version >1.0.1</version >
</dependency >
<dependency >
<groupId >org.webjars</groupId >
<artifactId >jquery</artifactId >
<version >1.9.1</version >
</dependency >
<dependency >
<groupId >org.apache.tomcat.embed</groupId >
<artifactId >tomcat-embed-jasper</artifactId >
<scope >provided</scope >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-tomcat</artifactId >
<scope >provided</scope >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-devtools</artifactId >
<scope >runtime</scope >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-test</artifactId >
<scope >test</scope >
</dependency >
</dependencies >
<build >
<finalName >todo-web-application-mysql</finalName >
<plugins >
<plugin >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-maven-plugin</artifactId >
</plugin >
<!-- Dockerfile Maven -->
<!-- https://github.com/spotify/dockerfile-maven -->
<plugin >
<groupId >com.spotify</groupId >
<artifactId >dockerfile-maven-plugin</artifactId >
<version >1.4.10</version >
<executions >
<execution >
<id >default</id >
<goals >
<goal >build</goal >
<!-- <goal>push</goal> -->
</goals >
</execution >
</executions >
<configuration >
<repository >in28min/${project.name}</repository >
<tag >${project.version}</tag >
<skipDockerInfo >true</skipDockerInfo >
</configuration >
</plugin >
</plugins >
</build >
<repositories >
<repository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestones</url >
</repository >
</repositories >
<pluginRepositories >
<pluginRepository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestones</url >
</pluginRepository >
</pluginRepositories >
</project >
/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/EnvironmentConfigurationLogger.java
package com .in28minutes .springboot .web ;
import java .util .Arrays ;
import java .util .stream .StreamSupport ;
import org .slf4j .Logger ;
import org .slf4j .LoggerFactory ;
import org .springframework .context .event .ContextRefreshedEvent ;
import org .springframework .context .event .EventListener ;
import org .springframework .core .env .AbstractEnvironment ;
import org .springframework .core .env .EnumerablePropertySource ;
import org .springframework .core .env .Environment ;
import org .springframework .core .env .MutablePropertySources ;
import org .springframework .stereotype .Component ;
@ Component
public class EnvironmentConfigurationLogger {
private static final Logger LOGGER = LoggerFactory .getLogger (EnvironmentConfigurationLogger .class );
@ SuppressWarnings ("rawtypes" )
@ EventListener
public void handleContextRefresh (ContextRefreshedEvent event ) {
final Environment environment = event .getApplicationContext ().getEnvironment ();
LOGGER .info ("====== Environment and configuration ======" );
LOGGER .info ("Active profiles: {}" , Arrays .toString (environment .getActiveProfiles ()));
final MutablePropertySources sources = ((AbstractEnvironment ) environment ).getPropertySources ();
StreamSupport .stream (sources .spliterator (), false ).filter (ps -> ps instanceof EnumerablePropertySource )
.map (ps -> ((EnumerablePropertySource ) ps ).getPropertyNames ()).flatMap (Arrays ::stream ).distinct ()
.forEach (prop -> {
LOGGER .info ("{}" , prop );
// Object resolved = environment.getProperty(prop, Object.class);
// if (resolved instanceof String) {
// LOGGER.info("{}", environment.getProperty(prop));
// }
});
LOGGER .info ("===========================================" );
}
}
/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java
package com .in28minutes .springboot .web ;
import org .springframework .boot .SpringApplication ;
import org .springframework .boot .autoconfigure .SpringBootApplication ;
import org .springframework .boot .builder .SpringApplicationBuilder ;
import org .springframework .boot .web .servlet .support .SpringBootServletInitializer ;
import org .springframework .context .annotation .ComponentScan ;
@ SpringBootApplication
@ ComponentScan ("com.in28minutes.springboot.web" )
public class SpringBootFirstWebApplication extends SpringBootServletInitializer {
@ Override
protected SpringApplicationBuilder configure (SpringApplicationBuilder application ) {
return application .sources (SpringBootFirstWebApplication .class );
}
public static void main (String [] args ) {
SpringApplication .run (SpringBootFirstWebApplication .class , args );
}
}
/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/ErrorController.java
package com .in28minutes .springboot .web .controller ;
import javax .servlet .http .HttpServletRequest ;
import org .springframework .stereotype .Controller ;
import org .springframework .web .bind .annotation .ExceptionHandler ;
import org .springframework .web .servlet .ModelAndView ;
@ Controller ("error" )
public class ErrorController {
@ ExceptionHandler (Exception .class )
public ModelAndView handleException
(HttpServletRequest request , Exception ex ){
ModelAndView mv = new ModelAndView ();
mv .addObject ("exception" , ex .getLocalizedMessage ());
mv .addObject ("url" , request .getRequestURL ());
mv .setViewName ("error" );
return mv ;
}
}
/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java
package com .in28minutes .springboot .web .controller ;
import javax .servlet .http .HttpServletRequest ;
import javax .servlet .http .HttpServletResponse ;
import org .springframework .security .core .Authentication ;
import org .springframework .security .core .context .SecurityContextHolder ;
import org .springframework .security .web .authentication .logout .SecurityContextLogoutHandler ;
import org .springframework .stereotype .Controller ;
import org .springframework .web .bind .annotation .RequestMapping ;
import org .springframework .web .bind .annotation .RequestMethod ;
@ Controller
public class LogoutController {
@ RequestMapping (value = "/logout" , method = RequestMethod .GET )
public String logout (HttpServletRequest request ,
HttpServletResponse response ) {
Authentication authentication = SecurityContextHolder .getContext ()
.getAuthentication ();
if (authentication != null ) {
new SecurityContextLogoutHandler ().logout (request , response ,
authentication );
}
return "redirect:/" ;
}
}
/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/TodoController.java
package com .in28minutes .springboot .web .controller ;
import java .text .SimpleDateFormat ;
import java .util .Date ;
import javax .validation .Valid ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .beans .propertyeditors .CustomDateEditor ;
import org .springframework .security .core .context .SecurityContextHolder ;
import org .springframework .security .core .userdetails .UserDetails ;
import org .springframework .stereotype .Controller ;
import org .springframework .ui .ModelMap ;
import org .springframework .validation .BindingResult ;
import org .springframework .web .bind .WebDataBinder ;
import org .springframework .web .bind .annotation .InitBinder ;
import org .springframework .web .bind .annotation .RequestMapping ;
import org .springframework .web .bind .annotation .RequestMethod ;
import org .springframework .web .bind .annotation .RequestParam ;
import com .in28minutes .springboot .web .model .Todo ;
import com .in28minutes .springboot .web .service .TodoRepository ;
@ Controller
public class TodoController {
@ Autowired
TodoRepository repository ;
@ InitBinder
public void initBinder (WebDataBinder binder ) {
// Date - dd/MM/yyyy
SimpleDateFormat dateFormat = new SimpleDateFormat ("dd/MM/yyyy" );
binder .registerCustomEditor (Date .class , new CustomDateEditor (
dateFormat , false ));
}
@ RequestMapping (value = "/list-todos" , method = RequestMethod .GET )
public String showTodos (ModelMap model ) {
String name = getLoggedInUserName (model );
model .put ("todos" , repository .findByUser (name ));
//model.put("todos", service.retrieveTodos(name));
return "list-todos" ;
}
private String getLoggedInUserName (ModelMap model ) {
Object principal = SecurityContextHolder .getContext ()
.getAuthentication ().getPrincipal ();
if (principal instanceof UserDetails ) {
return ((UserDetails ) principal ).getUsername ();
}
return principal .toString ();
}
@ RequestMapping (value = "/add-todo" , method = RequestMethod .GET )
public String showAddTodoPage (ModelMap model ) {
model .addAttribute ("todo" , new Todo (0 , getLoggedInUserName (model ),
"Default Desc" , new Date (), false ));
return "todo" ;
}
@ RequestMapping (value = "/delete-todo" , method = RequestMethod .GET )
public String deleteTodo (@ RequestParam int id ) {
//if(id==1)
//throw new RuntimeException("Something went wrong");
repository .deleteById (id );
//service.deleteTodo(id);
return "redirect:/list-todos" ;
}
@ RequestMapping (value = "/update-todo" , method = RequestMethod .GET )
public String showUpdateTodoPage (@ RequestParam int id , ModelMap model ) {
Todo todo = repository .findById (id ).get ();
//Todo todo = service.retrieveTodo(id);
model .put ("todo" , todo );
return "todo" ;
}
@ RequestMapping (value = "/update-todo" , method = RequestMethod .POST )
public String updateTodo (ModelMap model , @ Valid Todo todo ,
BindingResult result ) {
if (result .hasErrors ()) {
return "todo" ;
}
todo .setUser (getLoggedInUserName (model ));
repository .save (todo );
//service.updateTodo(todo);
return "redirect:/list-todos" ;
}
@ RequestMapping (value = "/add-todo" , method = RequestMethod .POST )
public String addTodo (ModelMap model , @ Valid Todo todo , BindingResult result ) {
if (result .hasErrors ()) {
return "todo" ;
}
todo .setUser (getLoggedInUserName (model ));
repository .save (todo );
/*service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(),
false);*/
return "redirect:/list-todos" ;
}
}
/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java
package com .in28minutes .springboot .web .controller ;
import org .springframework .security .core .context .SecurityContextHolder ;
import org .springframework .security .core .userdetails .UserDetails ;
import org .springframework .stereotype .Controller ;
import org .springframework .ui .ModelMap ;
import org .springframework .web .bind .annotation .RequestMapping ;
import org .springframework .web .bind .annotation .RequestMethod ;
@ Controller
public class WelcomeController {
@ RequestMapping (value = "/" , method = RequestMethod .GET )
public String showWelcomePage (ModelMap model ) {
model .put ("name" , getLoggedinUserName ());
return "welcome" ;
}
private String getLoggedinUserName () {
Object principal = SecurityContextHolder .getContext ()
.getAuthentication ().getPrincipal ();
if (principal instanceof UserDetails ) {
return ((UserDetails ) principal ).getUsername ();
}
return principal .toString ();
}
}
/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/model/Todo.java
package com .in28minutes .springboot .web .model ;
import java .util .Date ;
import javax .persistence .Column ;
import javax .persistence .Entity ;
import javax .persistence .GeneratedValue ;
import javax .persistence .Id ;
import javax .validation .constraints .Size ;
@ Entity
public class Todo {
@ Id
@ GeneratedValue
private int id ;
private String user ;
@ Size (min =10 , message ="Enter at least 10 Characters..." )
@ Column (name ="description" )
private String desc ;
private Date targetDate ;
private boolean isDone ;
public Todo () {
super ();
}
public Todo (int id , String user , String desc , Date targetDate ,
boolean isDone ) {
super ();
this .id = id ;
this .user = user ;
this .desc = desc ;
this .targetDate = targetDate ;
this .isDone = isDone ;
}
public int getId () {
return id ;
}
public void setId (int id ) {
this .id = id ;
}
public String getUser () {
return user ;
}
public void setUser (String user ) {
this .user = user ;
}
public String getDesc () {
return desc ;
}
public void setDesc (String desc ) {
this .desc = desc ;
}
public Date getTargetDate () {
return targetDate ;
}
public void setTargetDate (Date targetDate ) {
this .targetDate = targetDate ;
}
public boolean isDone () {
return isDone ;
}
public void setDone (boolean isDone ) {
this .isDone = isDone ;
}
@ Override
public int hashCode () {
final int prime = 31 ;
int result = 1 ;
result = prime * result + id ;
return result ;
}
@ Override
public boolean equals (Object obj ) {
if (this == obj ) {
return true ;
}
if (obj == null ) {
return false ;
}
if (getClass () != obj .getClass ()) {
return false ;
}
Todo other = (Todo ) obj ;
if (id != other .id ) {
return false ;
}
return true ;
}
@ Override
public String toString () {
return String .format (
"Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]" , id ,
user , desc , targetDate , isDone );
}
}
/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java
package com .in28minutes .springboot .web .security ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .context .annotation .Configuration ;
import org .springframework .security .config .annotation .authentication .builders .AuthenticationManagerBuilder ;
import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
import org .springframework .security .config .annotation .web .configuration .WebSecurityConfigurerAdapter ;
@ Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
//Create User - in28Minutes/dummy
@ Autowired
public void configureGlobalSecurity (AuthenticationManagerBuilder auth )
throws Exception {
auth .inMemoryAuthentication ().withUser ("in28minutes" ).password ("{noop}dummy" )
.roles ("USER" , "ADMIN" );
}
@ Override
protected void configure (HttpSecurity http ) throws Exception {
http .authorizeRequests ().antMatchers ("/login" , "/h2-console/**" ).permitAll ()
.antMatchers ("/" , "/*todo*/**" ).access ("hasRole('USER')" ).and ()
.formLogin ();
http .csrf ().disable ();
http .headers ().frameOptions ().disable ();
}
}
/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/service/TodoRepository.java
package com .in28minutes .springboot .web .service ;
import java .util .List ;
import org .springframework .data .jpa .repository .JpaRepository ;
import com .in28minutes .springboot .web .model .Todo ;
public interface TodoRepository extends JpaRepository <Todo , Integer >{
List <Todo > findByUser (String user );
//service.retrieveTodos(name)
//service.deleteTodo(id);
//service.retrieveTodo(id)
//service.updateTodo(todo)
//service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(),false);
}
/03-todo-web-application-mysql/src/main/java/com/in28minutes/springboot/web/service/TodoService.java
package com .in28minutes .springboot .web .service ;
import java .util .ArrayList ;
import java .util .Date ;
import java .util .Iterator ;
import java .util .List ;
import org .springframework .stereotype .Service ;
import com .in28minutes .springboot .web .model .Todo ;
@ Service
public class TodoService {
private static List <Todo > todos = new ArrayList <Todo >();
private static int todoCount = 3 ;
static {
todos .add (new Todo (1 , "in28minutes" , "Learn Spring MVC" , new Date (),
false ));
todos .add (new Todo (2 , "in28minutes" , "Learn Struts" , new Date (), false ));
todos .add (new Todo (3 , "in28minutes" , "Learn Hibernate" , new Date (),
false ));
}
public List <Todo > retrieveTodos (String user ) {
List <Todo > filteredTodos = new ArrayList <Todo >();
for (Todo todo : todos ) {
if (todo .getUser ().equalsIgnoreCase (user )) {
filteredTodos .add (todo );
}
}
return filteredTodos ;
}
public Todo retrieveTodo (int id ) {
for (Todo todo : todos ) {
if (todo .getId ()==id ) {
return todo ;
}
}
return null ;
}
public void updateTodo (Todo todo ){
todos .remove (todo );
todos .add (todo );
}
public void addTodo (String name , String desc , Date targetDate ,
boolean isDone ) {
todos .add (new Todo (++todoCount , name , desc , targetDate , isDone ));
}
public void deleteTodo (int id ) {
Iterator <Todo > iterator = todos .iterator ();
while (iterator .hasNext ()) {
Todo todo = iterator .next ();
if (todo .getId () == id ) {
iterator .remove ();
}
}
}
}
/03-todo-web-application-mysql/src/main/resources/application.properties
spring.mvc.view.prefix =/WEB-INF/jsp/
spring.mvc.view.suffix =.jsp
logging.level.org.springframework.web =INFO
management.endpoints.web.base-path =/manage
management.endpoints.web.exposure.include =*
spring.jpa.show-sql =true
# spring.h2.console.enabled=true
# spring.h2.console.settings.web-allow-others=true
spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto =update
spring.datasource.url =jdbc:mysql://${RDS_HOSTNAME:localhost}:${RDS_PORT:3306}/${RDS_DB_NAME:todos}
spring.datasource.username =${RDS_USERNAME:todos-user}
spring.datasource.password =${RDS_PASSWORD:dummytodos}
spring.jpa.properties.hibernate.dialect =org.hibernate.dialect.MySQL57Dialect
/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/common/footer.jspf
<script src="webjars/jquery/1.9.1/jquery.min.js"></script>
<script src="webjars/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script
src="webjars/bootstrap-datepicker/1.0.1/js/bootstrap-datepicker.js"></script>
<script>
$('#targetDate').datepicker({
format : 'dd/mm/yyyy'
});
</script>
</body>
</html>
/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/common/header.jspf
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<html>
<head>
<title>First Web Application</title>
<link href="webjars/bootstrap/3.3.6/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body>
/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/common/navigation.jspf
<nav role="navigation" class="navbar navbar-default">
<div class="">
<a href="http://www.in28minutes.com" class="navbar-brand">in28Minutes</a>
</div>
<div class="navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="./">Home</a></li>
<li><a href="./list-todos">Todos</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="./logout">Logout</a></li>
</ul>
</div>
</nav>
/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/error.jsp
<%@ include file="common/header.jspf"%>
<%@ include file="common/navigation.jspf"%>
<div class="container">
An exception occurred! Please contact Support!
</div>
<%@ include file="common/footer.jspf"%>
/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/list-todos.jsp
<%@ include file="common/header.jspf" %>
<%@ include file="common/navigation.jspf" %>
<div class="container">
<table class="table table-striped">
<caption>Your todos are</caption>
<thead>
<tr>
<th>Description</th>
<th>Target Date</th>
<th>Is it Done?</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<c:forEach items="${todos}" var="todo">
<tr>
<td>${todo.desc}</td>
<td><fmt:formatDate value="${todo.targetDate}" pattern="dd/MM/yyyy"/></td>
<td>${todo.done}</td>
<td><a type="button" class="btn btn-success"
href="./update-todo?id=${todo.id}">Update</a></td>
<td><a type="button" class="btn btn-warning"
href="./delete-todo?id=${todo.id}">Delete</a></td>
</tr>
</c:forEach>
</tbody>
</table>
<div>
<a class="button" href="./add-todo">Add a Todo</a>
</div>
</div>
<%@ include file="common/footer.jspf" %>
/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/todo.jsp
<%@ include file="common/header.jspf" %>
<%@ include file="common/navigation.jspf" %>
<div class="container">
<form:form method="post" modelAttribute="todo">
<form:hidden path="id" />
<fieldset class="form-group">
<form:label path="desc">Description</form:label>
<form:input path="desc" type="text" class="form-control"
required="required" />
<form:errors path="desc" cssClass="text-warning" />
</fieldset>
<fieldset class="form-group">
<form:label path="targetDate">Target Date</form:label>
<form:input path="targetDate" type="text" class="form-control"
required="required" />
<form:errors path="targetDate" cssClass="text-warning" />
</fieldset>
<button type="submit" class="btn btn-success">Add</button>
</form:form>
</div>
<%@ include file="common/footer.jspf" %>
/03-todo-web-application-mysql/src/main/webapp/WEB-INF/jsp/welcome.jsp
<%@ include file="common/header.jspf"%>
<%@ include file="common/navigation.jspf"%>
<div class="container">
Welcome ${name}!! <a href="./list-todos">Click here</a> to manage your
todo's.
</div>
<%@ include file="common/footer.jspf"%>
/03-todo-web-application-mysql/src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java
package com .in28minutes .springboot .web ;
import org .junit .Test ;
import org .junit .runner .RunWith ;
import org .springframework .boot .test .context .SpringBootTest ;
import org .springframework .test .context .junit4 .SpringRunner ;
@ RunWith (SpringRunner .class )
@ SpringBootTest
public class SpringBootFirstWebApplicationTests {
@ Test
public void contextLoads () {
}
}
/03-todo-web-application-mysql/src/test/resources/application.properties
spring.jpa.hibernate.ddl-auto =create-drop
spring.datasource.driver-class-name =org.h2.Driver
spring.datasource.url =jdbc:h2:mem:testdb; DB_CLOSE_DELAY=-1
spring.datasource.username =sa
spring.datasource.password =sa
/04-spring-boot-react-full-stack-h2/docker-compose.yml
version: '3.7'
# Removed subprocess.CalledProcessError: Command '['/usr/local/bin/docker-credential-desktop', 'get']' returned non-zero exit status 1
# I had this:
# cat ~/.docker/config.json
# {"auths":{},"credsStore":"", "credsStore":"desktop","stackOrchestrator":"swarm"}
# I updated to this:
# {"auths":{},"credsStore":"","stackOrchestrator":"swarm"}
services:
todo-frontend:
#image: in28min/todo-web-application-mysql:0.0.1-SNAPSHOT
build:
context: frontend/todo-app
#context: .
#dockerfile: Dockerfile
ports:
- "4000:80"
restart: always
depends_on: # Start the depends_on first
- todo-api
#environment:
#BACKEND_API_BASE_URL: http://localhost:8080
networks:
- fullstack-application-network
todo-api:
#image: in28min/todo-web-application-mysql:0.0.1-SNAPSHOT
build:
context: restful-web-services
#context: .
#dockerfile: Dockerfile
ports:
- "8080:8080"
restart: always
#depends_on: # Start the depends_on first
#- todo-api
networks:
- fullstack-application-network
# Networks to be created to facilitate communication between containers
networks:
fullstack-application-network:
/04-spring-boot-react-full-stack-h2/frontend/todo-app/Dockerfile
## Stage 1 - Lets build the "deployable package"
FROM node:7.10 as frontend-build
WORKDIR /fullstack/frontend
# Step 1 - Download all package dependencies first.
# We will redownload dependencies only when packages change.
COPY package.json package-lock.json ./
RUN npm install
# Step 2 - Copy all source and run build
COPY . ./
RUN npm run build
## Stage 2 - Let's build a minimal image with the "deployable package"
FROM nginx:1.12-alpine
COPY --from=frontend-build /fullstack/frontend/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
/04-spring-boot-react-full-stack-h2/frontend/todo-app/public/index.html
<!DOCTYPE html>
< html lang ="en ">
< head >
< meta charset ="utf-8 " />
< link rel ="shortcut icon " href ="%PUBLIC_URL%/favicon.ico " />
< meta
name ="viewport "
content ="width=device-width, initial-scale=1, shrink-to-fit=no "
/>
< meta name ="theme-color " content ="#000000 " />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
< link rel ="manifest " href ="%PUBLIC_URL%/manifest.json " />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
< title > My Todo Application</ title >
</ head >
< body >
< noscript > You need to enable JavaScript to run this app.</ noscript >
< div id ="root "> </ div >
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</ body >
</ html >
/04-spring-boot-react-full-stack-h2/frontend/todo-app/public/manifest.json
{
"short_name" : " React App" ,
"name" : " Create React App Sample" ,
"icons" : [
{
"src" : " favicon.ico" ,
"sizes" : " 64x64 32x32 24x24 16x16" ,
"type" : " image/x-icon"
}
],
"start_url" : " ." ,
"display" : " standalone" ,
"theme_color" : " #000000" ,
"background_color" : " #ffffff"
}
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/App.css
.footer {
position : absolute;
bottom : 0 ;
width : 100% ;
height : 40px ;
background-color : # 222222 ;
}
.App {
text-align : center;
}
.App-logo {
animation : App-logo-spin infinite 20s linear;
height : 40vmin ;
pointer-events : none;
}
.App-header {
background-color : # 282c34 ;
min-height : 100vh ;
display : flex;
flex-direction : column;
align-items : center;
justify-content : center;
font-size : calc (10px + 2vmin );
color : white;
}
.App-link {
color : # 61dafb ;
}
@keyframes App-logo-spin {
from {
transform : rotate (0deg );
}
to {
transform : rotate (360deg );
}
}
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/App.js
import React , { Component } from 'react' ;
//import FirstComponent from './components/learning-examples/FirstComponent'
//import SecondComponent from './components/learning-examples/SecondComponent'
//import ThirdComponent from './components/learning-examples/ThirdComponent'
//import Counter from './components/counter/Counter'
import TodoApp from './components/todo/TodoApp'
import './App.css' ;
import './bootstrap.css' ;
class App extends Component {
render ( ) {
return (
< div className = "App" >
{ /*<Counter/>*/ }
< TodoApp />
</ div >
) ;
}
}
// class LearningComponents extends Component {
// render() {
// return (
// <div className="LearningComponents">
// My Hello World
// <FirstComponent></FirstComponent>
// <SecondComponent></SecondComponent>
// <ThirdComponent></ThirdComponent>
// </div>
// );
// }
// }
export default App ;
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/App.test.js
import React from 'react' ;
import ReactDOM from 'react-dom' ;
import App from './App' ;
it ( 'renders without crashing' , ( ) => {
const div = document . createElement ( 'div' ) ;
ReactDOM . render ( < App /> , div ) ;
ReactDOM . unmountComponentAtNode ( div ) ;
} ) ;
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/Constants.js
/* For Best Practices https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables*/
export const API_URL = 'http://localhost:8080'
//export const API_URL = 'http://rest-api-full-stack-ranga-101.cfapps.io'
//export const API_URL = 'https://restful-web-services-full-stack-bankend.cfapps.io'
export const JPA_API_URL = `${ API_URL } /jpa`
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/api/todo/HelloWorldService.js
import axios from 'axios'
import { API_URL } from '../../Constants'
class HelloWorldService {
executeHelloWorldService ( ) {
//console.log('executed service')
return axios . get ( `${ API_URL } /hello-world` ) ;
}
executeHelloWorldBeanService ( ) {
//console.log('executed service')
return axios . get ( `${ API_URL } /hello-world-bean` ) ;
}
executeHelloWorldPathVariableService ( name ) {
//console.log('executed service')
// let username = 'in28minutes'
// let password = 'dummy'
// let basicAuthHeader = 'Basic ' + window.btoa(username + ":" + password)
return axios . get ( `${ API_URL } /hello-world/path-variable/${ name } `
// ,
// {
// headers : {
// authorization: basicAuthHeader
// }
// }
) ;
}
}
export default new HelloWorldService ( )
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/api/todo/TodoDataService.js
import axios from 'axios'
import { JPA_API_URL } from '../../Constants'
class TodoDataService {
retrieveAllTodos ( name ) {
//console.log('executed service')
return axios . get ( `${ JPA_API_URL } /users/${ name } /todos` ) ;
}
retrieveTodo ( name , id ) {
//console.log('executed service')
return axios . get ( `${ JPA_API_URL } /users/${ name } /todos/${ id } ` ) ;
}
deleteTodo ( name , id ) {
//console.log('executed service')
return axios . delete ( `${ JPA_API_URL } /users/${ name } /todos/${ id } ` ) ;
}
updateTodo ( name , id , todo ) {
//console.log('executed service')
return axios . put ( `${ JPA_API_URL } /users/${ name } /todos/${ id } ` , todo ) ;
}
createTodo ( name , todo ) {
//console.log('executed service')
return axios . post ( `${ JPA_API_URL } /users/${ name } /todos/` , todo ) ;
}
}
export default new TodoDataService ( )
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/bootstrap.css
@import url (https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css)
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/counter/Counter.css
/*
button {
background-color: green;
font-size : 16px;
padding : 15px 30px;
color : white;
width : 100px;
}
.count {
font-size : 50px;
padding : 15px 30px;
}
.reset {
background-color: red;
width : 200px;
}
body {
padding : 15px 30px;
}
*/
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/counter/Counter.jsx
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './Counter.css'
class Counter extends Component {
constructor() {
super(); //Error 1
this.state = {
counter: 0
}
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
this.reset = this.reset.bind(this);
}
render() {
return (
<div className="counter">
<CounterButton by={1} incrementMethod={this.increment} decrementMethod={this.decrement} />
<CounterButton by={5} incrementMethod={this.increment} decrementMethod={this.decrement} />
<CounterButton by={10} incrementMethod={this.increment} decrementMethod={this.decrement} />
<span className="count">{this.state.counter}</span>
<div><button className="reset" onClick={this.reset}>Reset</button></div>
</div>
);
}
reset() {
this.setState({ counter: 0 });
}
increment(by) {
//console.log(`increment from child - ${by}`)
this.setState(
(prevState) => {
return { counter: prevState.counter + by }
}
);
}
decrement(by) {
//console.log(`increment from child - ${by}`)
this.setState(
(prevState) => {
return { counter: prevState.counter - by }
}
);
}
}
class CounterButton extends Component {
//Define the initial state in a constructor
//state => counter 0
//constructor() {
// super(); //Error 1
// this.state = {
// counter : 0
// }
// this.increment = this.increment.bind(this);
// this.decrement = this.decrement.bind(this);
//}
render() {
//render = () => {
//const style = {fontSize : "50px", padding : "15px 30px"};
return (
<div className="counter">
<button onClick={() => this.props.incrementMethod(this.props.by)} >+{this.props.by}</button>
<button onClick={() => this.props.decrementMethod(this.props.by)} >-{this.props.by}</button>
{/*<span className="count"
>{this.state.counter}</span>*/}
</div>
)
}
// increment() { //Update state - counter++
// //console.log('increment');
// //this.state.counter++; //Bad Practice
// this.setState({
// counter: this.state.counter + this.props.by
// });
// this.props.incrementMethod(this.props.by);
// }
// decrement () {
// this.setState({
// counter: this.state.counter - this.props.by
// });
// this.props.decrementMethod(this.props.by);
// }
}
CounterButton.defaultProps = {
by: 1
}
CounterButton.propTypes = {
by: PropTypes.number
}
export default Counter
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/learning-examples/FirstComponent.jsx
import React, { Component } from 'react'
//Class Component
class FirstComponent extends Component {
render() {
return (
<div className="firstComponent">
FirstComponent
</div>
)
}
}
export default FirstComponent
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/learning-examples/SecondComponent.jsx
import React, { Component } from 'react'
class SecondComponent extends Component {
render() {
return (
<div className="secondComponent">
Second Component
</div>
)
}
}
export default SecondComponent
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/learning-examples/ThirdComponent.jsx
import React from 'react'
function ThirdComponent() {
return (
<div className="thirdComponent">
Third Component
</div>
)
}
export default ThirdComponent
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/todo/AuthenticatedRoute.jsx
import React, { Component } from 'react'
import { Route, Redirect } from 'react-router-dom'
import AuthenticationService from './AuthenticationService.js'
class AuthenticatedRoute extends Component {
render() {
if (AuthenticationService.isUserLoggedIn()) {
return <Route {...this.props} />
} else {
return <Redirect to="/login" />
}
}
}
export default AuthenticatedRoute
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/todo/AuthenticationService.js
import axios from 'axios'
import { API_URL } from '../../Constants'
export const USER_NAME_SESSION_ATTRIBUTE_NAME = 'authenticatedUser'
class AuthenticationService {
executeBasicAuthenticationService ( username , password ) {
return axios . get ( `${ API_URL } /basicauth` ,
{ headers : { authorization : this . createBasicAuthToken ( username , password ) } } )
}
executeJwtAuthenticationService ( username , password ) {
return axios . post ( `${ API_URL } /authenticate` , {
username,
password
} )
}
createBasicAuthToken ( username , password ) {
return 'Basic ' + window . btoa ( username + ":" + password )
}
registerSuccessfulLogin ( username , password ) {
//let basicAuthHeader = 'Basic ' + window.btoa(username + ":" + password)
//console.log('registerSuccessfulLogin')
sessionStorage . setItem ( USER_NAME_SESSION_ATTRIBUTE_NAME , username )
this . setupAxiosInterceptors ( this . createBasicAuthToken ( username , password ) )
}
registerSuccessfulLoginForJwt ( username , token ) {
sessionStorage . setItem ( USER_NAME_SESSION_ATTRIBUTE_NAME , username )
this . setupAxiosInterceptors ( this . createJWTToken ( token ) )
}
createJWTToken ( token ) {
return 'Bearer ' + token
}
logout ( ) {
sessionStorage . removeItem ( USER_NAME_SESSION_ATTRIBUTE_NAME ) ;
}
isUserLoggedIn ( ) {
let user = sessionStorage . getItem ( USER_NAME_SESSION_ATTRIBUTE_NAME )
if ( user === null ) return false
return true
}
getLoggedInUserName ( ) {
let user = sessionStorage . getItem ( USER_NAME_SESSION_ATTRIBUTE_NAME )
if ( user === null ) return ''
return user
}
setupAxiosInterceptors ( token ) {
axios . interceptors . request . use (
( config ) => {
if ( this . isUserLoggedIn ( ) ) {
config . headers . authorization = token
}
return config
}
)
}
}
export default new AuthenticationService ( )
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/todo/ErrorComponent.jsx
import React from 'react'
function ErrorComponent() {
return <div>An Error Occurred. I don't know what to do! Contact support at abcd-efgh-ijkl</div>
}
export default ErrorComponent
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/todo/FooterComponent.jsx
import React, { Component } from 'react'
class FooterComponent extends Component {
render() {
return (
<footer className="footer">
<span className="text-muted">All Rights Reserved 2018 @in28minutes</span>
</footer>
)
}
}
export default FooterComponent
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/todo/HeaderComponent.jsx
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import AuthenticationService from './AuthenticationService.js'
class HeaderComponent extends Component {
render() {
const isUserLoggedIn = AuthenticationService.isUserLoggedIn();
//console.log(isUserLoggedIn);
return (
<header>
<nav className="navbar navbar-expand-md navbar-dark bg-dark">
<div><a href="http://www.in28minutes.com" className="navbar-brand">in28Minutes</a></div>
<ul className="navbar-nav">
{isUserLoggedIn && <li><Link className="nav-link" to="/welcome/in28minutes">Home</Link></li>}
{isUserLoggedIn && <li><Link className="nav-link" to="/todos">Todos</Link></li>}
</ul>
<ul className="navbar-nav navbar-collapse justify-content-end">
{!isUserLoggedIn && <li><Link className="nav-link" to="/login">Login</Link></li>}
{isUserLoggedIn && <li><Link className="nav-link" to="/logout" onClick={AuthenticationService.logout}>Logout</Link></li>}
</ul>
</nav>
</header>
)
}
}
export default HeaderComponent
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/todo/ListTodosComponent.jsx
import React, { Component } from 'react'
import TodoDataService from '../../api/todo/TodoDataService.js'
import AuthenticationService from './AuthenticationService.js'
import moment from 'moment'
class ListTodosComponent extends Component {
constructor(props) {
console.log('constructor')
super(props)
this.state = {
todos: [],
message: null
}
this.deleteTodoClicked = this.deleteTodoClicked.bind(this)
this.updateTodoClicked = this.updateTodoClicked.bind(this)
this.addTodoClicked = this.addTodoClicked.bind(this)
this.refreshTodos = this.refreshTodos.bind(this)
}
componentWillUnmount() {
console.log('componentWillUnmount')
}
shouldComponentUpdate(nextProps, nextState) {
console.log('shouldComponentUpdate')
console.log(nextProps)
console.log(nextState)
return true
}
componentDidMount() {
console.log('componentDidMount')
this.refreshTodos();
console.log(this.state)
}
refreshTodos() {
let username = AuthenticationService.getLoggedInUserName()
TodoDataService.retrieveAllTodos(username)
.then(
response => {
//console.log(response);
this.setState({ todos: response.data })
}
)
}
deleteTodoClicked(id) {
let username = AuthenticationService.getLoggedInUserName()
//console.log(id + " " + username);
TodoDataService.deleteTodo(username, id)
.then(
response => {
this.setState({ message: `Delete of todo ${id} Successful` })
this.refreshTodos()
}
)
}
addTodoClicked() {
this.props.history.push(`/todos/-1`)
}
updateTodoClicked(id) {
console.log('update ' + id)
this.props.history.push(`/todos/${id}`)
// /todos/${id}
// let username = AuthenticationService.getLoggedInUserName()
// //console.log(id + " " + username);
// TodoDataService.deleteTodo(username, id)
// .then (
// response => {
// this.setState({message : `Delete of todo ${id} Successful`})
// this.refreshTodos()
// }
// )
}
render() {
console.log('render')
return (
<div>
<h1>List Todos</h1>
{this.state.message && <div class="alert alert-success">{this.state.message}</div>}
<div className="container">
<table className="table">
<thead>
<tr>
<th>Description</th>
<th>Target Date</th>
<th>IsCompleted?</th>
<th>Update</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{
this.state.todos.map(
todo =>
<tr key={todo.id}>
<td>{todo.description}</td>
<td>{moment(todo.targetDate).format('YYYY-MM-DD')}</td>
<td>{todo.done.toString()}</td>
<td><button className="btn btn-success" onClick={() => this.updateTodoClicked(todo.id)}>Update</button></td>
<td><button className="btn btn-warning" onClick={() => this.deleteTodoClicked(todo.id)}>Delete</button></td>
</tr>
)
}
</tbody>
</table>
<div className="row">
<button className="btn btn-success" onClick={this.addTodoClicked}>Add</button>
</div>
</div>
</div>
)
}
}
export default ListTodosComponent
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/todo/LoginComponent.jsx
import React, { Component } from 'react'
import AuthenticationService from './AuthenticationService.js'
class LoginComponent extends Component {
constructor(props) {
super(props)
this.state = {
username: 'in28minutes',
password: '',
hasLoginFailed: false,
showSuccessMessage: false
}
// this.handleUsernameChange = this.handleUsernameChange.bind(this)
// this.handlePasswordChange = this.handlePasswordChange.bind(this)
this.handleChange = this.handleChange.bind(this)
this.loginClicked = this.loginClicked.bind(this)
}
handleChange(event) {
//console.log(this.state);
this.setState(
{
[event.target.name]
: event.target.value
}
)
}
// handleUsernameChange(event) {
// console.log(event.target.name);
// this.setState(
// {
// [event.target.name]
// :event.target.value
// }
// )
// }
// handlePasswordChange(event) {
// console.log(event.target.value);
// this.setState({password:event.target.value})
// }
loginClicked() {
//in28minutes,dummy
// if(this.state.username==='in28minutes' && this.state.password==='dummy'){
// AuthenticationService.registerSuccessfulLogin(this.state.username,this.state.password)
// this.props.history.push(`/welcome/${this.state.username}`)
// //this.setState({showSuccessMessage:true})
// //this.setState({hasLoginFailed:false})
// }
// else {
// this.setState({showSuccessMessage:false})
// this.setState({hasLoginFailed:true})
// }
// AuthenticationService
// .executeBasicAuthenticationService(this.state.username, this.state.password)
// .then(() => {
// AuthenticationService.registerSuccessfulLogin(this.state.username,this.state.password)
// this.props.history.push(`/welcome/${this.state.username}`)
// }).catch( () =>{
// this.setState({showSuccessMessage:false})
// this.setState({hasLoginFailed:true})
// })
AuthenticationService
.executeJwtAuthenticationService(this.state.username, this.state.password)
.then((response) => {
AuthenticationService.registerSuccessfulLoginForJwt(this.state.username, response.data.token)
this.props.history.push(`/welcome/${this.state.username}`)
}).catch(() => {
this.setState({ showSuccessMessage: false })
this.setState({ hasLoginFailed: true })
})
}
render() {
return (
<div>
<h1>Login</h1>
<div className="container">
{/*<ShowInvalidCredentials hasLoginFailed={this.state.hasLoginFailed}/>*/}
{this.state.hasLoginFailed && <div className="alert alert-warning">Invalid Credentials</div>}
{this.state.showSuccessMessage && <div>Login Sucessful</div>}
{/*<ShowLoginSuccessMessage showSuccessMessage={this.state.showSuccessMessage}/>*/}
User Name: <input type="text" name="username" value={this.state.username} onChange={this.handleChange} />
Password: <input type="password" name="password" value={this.state.password} onChange={this.handleChange} />
<button className="btn btn-success" onClick={this.loginClicked}>Login</button>
</div>
</div>
)
}
}
export default LoginComponent
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/todo/LogoutComponent.jsx
import React, { Component } from 'react'
class LogoutComponent extends Component {
render() {
return (
<>
<h1>You are logged out</h1>
<div className="container">
Thank You for Using Our Application.
</div>
</>
)
}
}
export default LogoutComponent
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/todo/TodoApp.jsx
import React, {Component} from 'react'
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'
import AuthenticatedRoute from './AuthenticatedRoute.jsx'
import LoginComponent from './LoginComponent.jsx'
import ListTodosComponent from './ListTodosComponent.jsx'
import ErrorComponent from './ErrorComponent.jsx'
import HeaderComponent from './HeaderComponent.jsx'
import FooterComponent from './FooterComponent.jsx'
import LogoutComponent from './LogoutComponent.jsx'
import WelcomeComponent from './WelcomeComponent.jsx'
import TodoComponent from './TodoComponent.jsx'
class TodoApp extends Component {
render() {
return (
<div className="TodoApp">
<Router>
<>
<HeaderComponent/>
<Switch>
<Route path="/" exact component={LoginComponent}/>
<Route path="/login" component={LoginComponent}/>
<AuthenticatedRoute path="/welcome/:name" component={WelcomeComponent}/>
<AuthenticatedRoute path="/todos/:id" component={TodoComponent}/>
<AuthenticatedRoute path="/todos" component={ListTodosComponent}/>
<AuthenticatedRoute path="/logout" component={LogoutComponent}/>
<Route component={ErrorComponent}/>
</Switch>
<FooterComponent/>
</>
</Router>
{/*<LoginComponent/>
<WelcomeComponent/>*/}
</div>
)
}
}
export default TodoApp
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/todo/TodoComponent.jsx
import React, { Component } from 'react'
import moment from 'moment'
import { Formik, Form, Field, ErrorMessage } from 'formik';
import TodoDataService from '../../api/todo/TodoDataService.js'
import AuthenticationService from './AuthenticationService.js'
class TodoComponent extends Component {
constructor(props) {
super(props)
this.state = {
id: this.props.match.params.id,
description: '',
targetDate: moment(new Date()).format('YYYY-MM-DD')
}
this.onSubmit = this.onSubmit.bind(this)
this.validate = this.validate.bind(this)
}
componentDidMount() {
if (this.state.id === -1) {
return
}
let username = AuthenticationService.getLoggedInUserName()
TodoDataService.retrieveTodo(username, this.state.id)
.then(response => this.setState({
description: response.data.description,
targetDate: moment(response.data.targetDate).format('YYYY-MM-DD')
}))
}
validate(values) {
let errors = {}
if (!values.description) {
errors.description = 'Enter a Description'
} else if (values.description.length < 5) {
errors.description = 'Enter atleast 5 Characters in Description'
}
if (!moment(values.targetDate).isValid()) {
errors.targetDate = 'Enter a valid Target Date'
}
return errors
}
onSubmit(values) {
let username = AuthenticationService.getLoggedInUserName()
let todo = {
id: this.state.id,
description: values.description,
targetDate: values.targetDate
}
if (this.state.id === -1) {
TodoDataService.createTodo(username, todo)
.then(() => this.props.history.push('/todos'))
} else {
TodoDataService.updateTodo(username, this.state.id, todo)
.then(() => this.props.history.push('/todos'))
}
console.log(values);
}
render() {
let { description, targetDate } = this.state
//let targetDate = this.state.targetDate
return (
<div>
<h1>Todo</h1>
<div className="container">
<Formik
initialValues={{ description, targetDate }}
onSubmit={this.onSubmit}
validateOnChange={false}
validateOnBlur={false}
validate={this.validate}
enableReinitialize={true}
>
{
(props) => (
<Form>
<ErrorMessage name="description" component="div"
className="alert alert-warning" />
<ErrorMessage name="targetDate" component="div"
className="alert alert-warning" />
<fieldset className="form-group">
<label>Description</label>
<Field className="form-control" type="text" name="description" />
</fieldset>
<fieldset className="form-group">
<label>Target Date</label>
<Field className="form-control" type="date" name="targetDate" />
</fieldset>
<button className="btn btn-success" type="submit">Save</button>
</Form>
)
}
</Formik>
</div>
</div>
)
}
}
export default TodoComponent
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/components/todo/WelcomeComponent.jsx
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import HelloWorldService from '../../api/todo/HelloWorldService.js'
class WelcomeComponent extends Component {
constructor(props) {
super(props)
this.retrieveWelcomeMessage = this.retrieveWelcomeMessage.bind(this)
this.state = {
welcomeMessage: ''
}
this.handleSuccessfulResponse = this.handleSuccessfulResponse.bind(this)
this.handleError = this.handleError.bind(this)
}
render() {
return (
<>
<h1>Welcome!</h1>
<div className="container">
Welcome {this.props.match.params.name}.
You can manage your todos <Link to="/todos">here</Link>.
</div>
<div className="container">
Click here to get a customized welcome message.
<button onClick={this.retrieveWelcomeMessage}
className="btn btn-success">Get Welcome Message</button>
</div>
<div className="container">
{this.state.welcomeMessage}
</div>
</>
)
}
retrieveWelcomeMessage() {
// HelloWorldService.executeHelloWorldService()
// .then( response => this.handleSuccessfulResponse(response) )
// HelloWorldService.executeHelloWorldBeanService()
// .then( response => this.handleSuccessfulResponse(response) )
HelloWorldService.executeHelloWorldPathVariableService(this.props.match.params.name)
.then(response => this.handleSuccessfulResponse(response))
.catch(error => this.handleError(error))
}
handleSuccessfulResponse(response) {
console.log(response)
this.setState({ welcomeMessage: response.data.message })
}
handleError(error) {
console.log(error.response)
let errorMessage = '';
if (error.message)
errorMessage += error.message
if (error.response && error.response.data) {
errorMessage += error.response.data.message
}
this.setState({ welcomeMessage: errorMessage })
}
}
export default WelcomeComponent
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/index.css
body {
margin : 0 ;
padding : 0 ;
font-family : -apple-system, BlinkMacSystemFont, "Segoe UI" , "Roboto" , "Oxygen" ,
"Ubuntu" , "Cantarell" , "Fira Sans" , "Droid Sans" , "Helvetica Neue" ,
sans-serif;
-webkit-font-smoothing : antialiased;
-moz-osx-font-smoothing : grayscale;
}
code {
font-family : source-code-pro, Menlo, Monaco, Consolas, "Courier New" ,
monospace;
}
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/index.js
import React from 'react' ;
import ReactDOM from 'react-dom' ;
import './index.css' ;
import App from './App' ;
import * as serviceWorker from './serviceWorker' ;
ReactDOM . render ( < App /> , document . getElementById ( 'root' ) ) ;
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker . unregister ( ) ;
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/logo.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>
/04-spring-boot-react-full-stack-h2/frontend/todo-app/src/serviceWorker.js
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean (
window . location . hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window . location . hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window . location . hostname . match (
/ ^ 1 2 7 (?: \. (?: 2 5 [ 0 - 5 ] | 2 [ 0 - 4 ] [ 0 - 9 ] | [ 0 1 ] ? [ 0 - 9 ] [ 0 - 9 ] ? ) ) { 3 } $ /
)
) ;
export function register ( config ) {
if ( process . env . NODE_ENV === 'production' && 'serviceWorker' in navigator ) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL ( process . env . PUBLIC_URL , window . location . href ) ;
if ( publicUrl . origin !== window . location . origin ) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return ;
}
window . addEventListener ( 'load' , ( ) => {
const swUrl = `${ process . env . PUBLIC_URL } /service-worker.js` ;
if ( isLocalhost ) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker ( swUrl , config ) ;
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator . serviceWorker . ready . then ( ( ) => {
console . log (
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA'
) ;
} ) ;
} else {
// Is not localhost. Just register service worker
registerValidSW ( swUrl , config ) ;
}
} ) ;
}
}
function registerValidSW ( swUrl , config ) {
navigator . serviceWorker
. register ( swUrl )
. then ( registration => {
registration . onupdatefound = ( ) => {
const installingWorker = registration . installing ;
if ( installingWorker == null ) {
return ;
}
installingWorker . onstatechange = ( ) => {
if ( installingWorker . state === 'installed' ) {
if ( navigator . serviceWorker . controller ) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console . log (
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
) ;
// Execute callback
if ( config && config . onUpdate ) {
config . onUpdate ( registration ) ;
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console . log ( 'Content is cached for offline use.' ) ;
// Execute callback
if ( config && config . onSuccess ) {
config . onSuccess ( registration ) ;
}
}
}
} ;
} ;
} )
. catch ( error => {
console . error ( 'Error during service worker registration:' , error ) ;
} ) ;
}
function checkValidServiceWorker ( swUrl , config ) {
// Check if the service worker can be found. If it can't reload the page.
fetch ( swUrl )
. then ( response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response . headers . get ( 'content-type' ) ;
if (
response . status === 404 ||
( contentType != null && contentType . indexOf ( 'javascript' ) === - 1 )
) {
// No service worker found. Probably a different app. Reload the page.
navigator . serviceWorker . ready . then ( registration => {
registration . unregister ( ) . then ( ( ) => {
window . location . reload ( ) ;
} ) ;
} ) ;
} else {
// Service worker found. Proceed as normal.
registerValidSW ( swUrl , config ) ;
}
} )
. catch ( ( ) => {
console . log (
'No internet connection found. App is running in offline mode.'
) ;
} ) ;
}
export function unregister ( ) {
if ( 'serviceWorker' in navigator ) {
navigator . serviceWorker . ready . then ( registration => {
registration . unregister ( ) ;
} ) ;
}
}
/04-spring-boot-react-full-stack-h2/frontend/todo-app/package.json
{
"name" : " todo-app" ,
"version" : " 0.1.0" ,
"private" : true ,
"dependencies" : {
"axios" : " ^0.18.0" ,
"formik" : " ^1.5.1" ,
"moment" : " ^2.24.0" ,
"react" : " ^16.8.4" ,
"react-dom" : " ^16.8.4" ,
"react-router-dom" : " ^4.3.1" ,
"react-scripts" : " 2.1.8"
},
"scripts" : {
"start" : " PORT=4200 react-scripts start" ,
"build" : " react-scripts build" ,
"test" : " react-scripts test" ,
"eject" : " react-scripts eject"
},
"eslintConfig" : {
"extends" : " react-app"
},
"browserslist" : [
" >0.2%" ,
" not dead" ,
" not ie <= 11" ,
" not op_mini all"
]
}
/04-spring-boot-react-full-stack-h2/restful-web-services/Dockerfile
## Stage 1 - Lets build the "deployable package"
FROM maven:3.6.1-jdk-8-alpine as backend-build
WORKDIR /fullstack/backend
# Step 1 - Copy pom.xml and download project dependencies
# Dividing copy into two steps to ensure that we download dependencies
# only when pom.xml changes
COPY pom.xml .
# dependency:go-offline - Goal that resolves all project dependencies,
# including plugins and reports and their dependencies. -B -> Batch mode
RUN mvn dependency:go-offline -B
# Step 2 - Copy source and build "deployable package"
COPY src src
RUN mvn install -DskipTests
# Unzip
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
## Stage 2 - Let's build a minimal image with the "deployable package"
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/fullstack/backend/target/dependency
COPY --from=backend-build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=backend-build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=backend-build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","com.in28minutes.rest.webservices.restfulwebservices.RestfulWebServicesApplication"]
/04-spring-boot-react-full-stack-h2/restful-web-services/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.in28minutes.rest.webservices</groupId >
<artifactId >04-rest-api-full-stack</artifactId >
<version >0.0.1-SNAPSHOT</version >
<packaging >jar</packaging >
<name >rest-api-full-stack</name >
<description >Demo project for Spring Boot</description >
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-parent</artifactId >
<version >2.1.7.RELEASE</version >
<relativePath /> <!-- lookup parent from repository -->
</parent >
<properties >
<project .build.sourceEncoding>UTF-8</project .build.sourceEncoding>
<project .reporting.outputEncoding>UTF-8</project .reporting.outputEncoding>
<java .version>1.8</java .version>
<maven-jar-plugin .version>3.1.1</maven-jar-plugin .version>
</properties >
<dependencies >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-data-jpa</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-security</artifactId >
</dependency >
<dependency >
<groupId >io.jsonwebtoken</groupId >
<artifactId >jjwt</artifactId >
<version >0.9.1</version >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-devtools</artifactId >
<scope >runtime</scope >
</dependency >
<dependency >
<groupId >com.h2database</groupId >
<artifactId >h2</artifactId >
<scope >runtime</scope >
</dependency >
<dependency >
<groupId >javax.xml.bind</groupId >
<artifactId >jaxb-api</artifactId >
</dependency >
<dependency >
<groupId >com.sun.xml.bind</groupId >
<artifactId >jaxb-impl</artifactId >
<version >2.3.1</version >
</dependency >
<dependency >
<groupId >org.glassfish.jaxb</groupId >
<artifactId >jaxb-runtime</artifactId >
</dependency >
<dependency >
<groupId >javax.activation</groupId >
<artifactId >activation</artifactId >
<version >1.1.1</version >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-test</artifactId >
<scope >test</scope >
</dependency >
</dependencies >
<build >
<finalName >rest-api-full-stack</finalName >
<plugins >
<plugin >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-maven-plugin</artifactId >
</plugin >
<!-- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
-->
<!-- Dockerfile Maven -->
<!-- https://github.com/spotify/dockerfile-maven -->
<!-- <plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.10</version>
<executions>
<execution>
<id>default</id>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<configuration>
<repository>in28min/${project.name}</repository>
<tag>${project.version}</tag>
<skipDockerInfo>true</skipDockerInfo>
</configuration>
</plugin>
--> </plugins >
</build >
<repositories >
<repository >
<id >spring-snapshots</id >
<name >Spring Snapshots</name >
<url >https://repo.spring.io/snapshot</url >
<snapshots >
<enabled >true</enabled >
</snapshots >
</repository >
<repository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestone</url >
<snapshots >
<enabled >false</enabled >
</snapshots >
</repository >
</repositories >
<pluginRepositories >
<pluginRepository >
<id >spring-snapshots</id >
<name >Spring Snapshots</name >
<url >https://repo.spring.io/snapshot</url >
<snapshots >
<enabled >true</enabled >
</snapshots >
</pluginRepository >
<pluginRepository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestone</url >
<snapshots >
<enabled >false</enabled >
</snapshots >
</pluginRepository >
</pluginRepositories >
</project >
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/basic/auth/AuthenticationBean.java
package com .in28minutes .rest .basic .auth ;
public class AuthenticationBean {
private String message ;
public AuthenticationBean (String message ) {
this .message = message ;
}
public String getMessage () {
return message ;
}
public void setMessage (String message ) {
this .message = message ;
}
@ Override
public String toString () {
return String .format ("HelloWorldBean [message=%s]" , message );
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/basic/auth/BasicAuthenticationController.java
package com .in28minutes .rest .basic .auth ;
import org .springframework .web .bind .annotation .GetMapping ;
import org .springframework .web .bind .annotation .RestController ;
//Controller
//@CrossOrigin(origins="http://localhost:4200")Replace with global config
@ RestController
public class BasicAuthenticationController {
@ GetMapping (path = "/basicauth" )
public AuthenticationBean helloWorldBean () {
//throw new RuntimeException("Some Error has Happened! Contact Support at ***-***");
return new AuthenticationBean ("You are authenticated" );
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/basic/auth/SpringSecurityConfigurationBasicAuth.java
package com .in28minutes .rest .basic .auth ;
import org .springframework .context .annotation .Configuration ;
import org .springframework .http .HttpMethod ;
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 .configuration .WebSecurityConfigurerAdapter ;
@ Configuration
@ EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter {
@ Override
protected void configure (HttpSecurity http ) throws Exception {
http
.csrf ().disable ()
.authorizeRequests ()
.antMatchers (HttpMethod .OPTIONS ,"/**" ).permitAll ()
.anyRequest ().authenticated ()
.and ()
//.formLogin().and()
.httpBasic ();
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplication.java
package com .in28minutes .rest .webservices .restfulwebservices ;
import org .springframework .boot .SpringApplication ;
import org .springframework .boot .autoconfigure .SpringBootApplication ;
import org .springframework .context .annotation .Bean ;
import org .springframework .web .servlet .config .annotation .CorsRegistry ;
import org .springframework .web .servlet .config .annotation .WebMvcConfigurer ;
@ SpringBootApplication
public class RestfulWebServicesApplication {
@ Bean
public WebMvcConfigurer corsConfigurer () {
return new WebMvcConfigurer () {
@ Override
public void addCorsMappings (CorsRegistry registry ) {
registry .addMapping ("/**" ).allowedMethods ("*" ).allowedOrigins ("*" );
}
};
}
public static void main (String [] args ) {
SpringApplication .run (RestfulWebServicesApplication .class , args );
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/helloworld/HelloWorldBean.java
package com .in28minutes .rest .webservices .restfulwebservices .helloworld ;
public class HelloWorldBean {
private String message ;
public HelloWorldBean (String message ) {
this .message = message ;
}
public String getMessage () {
return message ;
}
public void setMessage (String message ) {
this .message = message ;
}
@ Override
public String toString () {
return String .format ("Hello World Bean [message=%s]" , message );
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/helloworld/HelloWorldController.java
package com .in28minutes .rest .webservices .restfulwebservices .helloworld ;
import org .springframework .web .bind .annotation .GetMapping ;
import org .springframework .web .bind .annotation .PathVariable ;
import org .springframework .web .bind .annotation .RestController ;
@ RestController
public class HelloWorldController {
@ GetMapping (path = "/hello-world" )
public String helloWorld () {
return "Hello World" ;
}
@ GetMapping (path = "/hello-world-bean" )
public HelloWorldBean helloWorldBean () {
return new HelloWorldBean ("Hello World" );
}
///hello-world/path-variable/in28minutes
@ GetMapping (path = "/hello-world/path-variable/{name}" )
public HelloWorldBean helloWorldPathVariable (@ PathVariable String name ) {
return new HelloWorldBean (String .format ("Hello World, %s" , name ));
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/JWTWebSecurityConfig.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .beans .factory .annotation .Value ;
import org .springframework .context .annotation .Bean ;
import org .springframework .context .annotation .Configuration ;
import org .springframework .http .HttpMethod ;
import org .springframework .security .authentication .AuthenticationManager ;
import org .springframework .security .config .annotation .authentication .builders .AuthenticationManagerBuilder ;
import org .springframework .security .config .annotation .method .configuration .EnableGlobalMethodSecurity ;
import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
import org .springframework .security .config .annotation .web .builders .WebSecurity ;
import org .springframework .security .config .annotation .web .configuration .EnableWebSecurity ;
import org .springframework .security .config .annotation .web .configuration .WebSecurityConfigurerAdapter ;
import org .springframework .security .config .http .SessionCreationPolicy ;
import org .springframework .security .core .userdetails .UserDetailsService ;
import org .springframework .security .crypto .bcrypt .BCryptPasswordEncoder ;
import org .springframework .security .crypto .password .PasswordEncoder ;
import org .springframework .security .web .authentication .UsernamePasswordAuthenticationFilter ;
@ Configuration
@ EnableWebSecurity
@ EnableGlobalMethodSecurity (prePostEnabled = true )
public class JWTWebSecurityConfig extends WebSecurityConfigurerAdapter {
@ Autowired
private JwtUnAuthorizedResponseAuthenticationEntryPoint jwtUnAuthorizedResponseAuthenticationEntryPoint ;
@ Autowired
private UserDetailsService jwtInMemoryUserDetailsService ;
@ Autowired
private JwtTokenAuthorizationOncePerRequestFilter jwtAuthenticationTokenFilter ;
@ Value ("${jwt.get.token.uri}" )
private String authenticationPath ;
@ Autowired
public void configureGlobal (AuthenticationManagerBuilder auth ) throws Exception {
auth
.userDetailsService (jwtInMemoryUserDetailsService )
.passwordEncoder (passwordEncoderBean ());
}
@ Bean
public PasswordEncoder passwordEncoderBean () {
return new BCryptPasswordEncoder ();
}
@ Bean
@ Override
public AuthenticationManager authenticationManagerBean () throws Exception {
return super .authenticationManagerBean ();
}
@ Override
protected void configure (HttpSecurity httpSecurity ) throws Exception {
httpSecurity
.csrf ().disable ()
.exceptionHandling ().authenticationEntryPoint (jwtUnAuthorizedResponseAuthenticationEntryPoint ).and ()
.sessionManagement ().sessionCreationPolicy (SessionCreationPolicy .STATELESS ).and ()
.authorizeRequests ()
.anyRequest ().authenticated ();
httpSecurity
.addFilterBefore (jwtAuthenticationTokenFilter , UsernamePasswordAuthenticationFilter .class );
httpSecurity
.headers ()
.frameOptions ().sameOrigin () //H2 Console Needs this setting
.cacheControl (); //disable caching
}
@ Override
public void configure (WebSecurity webSecurity ) throws Exception {
webSecurity
.ignoring ()
.antMatchers (
HttpMethod .POST ,
authenticationPath
)
.antMatchers (HttpMethod .OPTIONS , "/**" )
.and ()
.ignoring ()
.antMatchers (
HttpMethod .GET ,
"/" //Other Stuff You want to Ignore
)
.and ()
.ignoring ()
.antMatchers ("/h2-console/**/**" );//Should not be in Production!
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/JwtInMemoryUserDetailsService.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt ;
import java .util .ArrayList ;
import java .util .List ;
import java .util .Optional ;
import org .springframework .security .core .userdetails .UserDetails ;
import org .springframework .security .core .userdetails .UserDetailsService ;
import org .springframework .security .core .userdetails .UsernameNotFoundException ;
//This is not used!
//@Service
public class JwtInMemoryUserDetailsService implements UserDetailsService {
static List <JwtUserDetails > inMemoryUserList = new ArrayList <>();
static {
inMemoryUserList .add (new JwtUserDetails (1L , "in28minutes" ,
"$2a$10$3zHzb.Npv1hfZbLEU5qsdOju/tk2je6W6PnNnY.c1ujWPcZh4PL6e" , "ROLE_USER_2" ));
}
@ Override
public UserDetails loadUserByUsername (String username ) throws UsernameNotFoundException {
Optional <JwtUserDetails > findFirst = inMemoryUserList .stream ()
.filter (user -> user .getUsername ().equals (username )).findFirst ();
if (!findFirst .isPresent ()) {
throw new UsernameNotFoundException (String .format ("USER_NOT_FOUND '%s'." , username ));
}
return findFirst .get ();
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/JwtTokenAuthorizationOncePerRequestFilter.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt ;
import java .io .IOException ;
import javax .servlet .FilterChain ;
import javax .servlet .ServletException ;
import javax .servlet .http .HttpServletRequest ;
import javax .servlet .http .HttpServletResponse ;
import org .slf4j .Logger ;
import org .slf4j .LoggerFactory ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .beans .factory .annotation .Value ;
import org .springframework .security .authentication .UsernamePasswordAuthenticationToken ;
import org .springframework .security .core .context .SecurityContextHolder ;
import org .springframework .security .core .userdetails .UserDetails ;
import org .springframework .security .core .userdetails .UserDetailsService ;
import org .springframework .security .web .authentication .WebAuthenticationDetailsSource ;
import org .springframework .stereotype .Component ;
import org .springframework .web .filter .OncePerRequestFilter ;
import io .jsonwebtoken .ExpiredJwtException ;
@ Component
public class JwtTokenAuthorizationOncePerRequestFilter extends OncePerRequestFilter {
private final Logger logger = LoggerFactory .getLogger (this .getClass ());
@ Autowired
private UserDetailsService jwtInMemoryUserDetailsService ;
@ Autowired
private JwtTokenUtil jwtTokenUtil ;
@ Value ("${jwt.http.request.header}" )
private String tokenHeader ;
@ Override
protected void doFilterInternal (HttpServletRequest request , HttpServletResponse response , FilterChain chain ) throws ServletException , IOException {
logger .debug ("Authentication Request For '{}'" , request .getRequestURL ());
final String requestTokenHeader = request .getHeader (this .tokenHeader );
String username = null ;
String jwtToken = null ;
if (requestTokenHeader != null && requestTokenHeader .startsWith ("Bearer " )) {
jwtToken = requestTokenHeader .substring (7 );
try {
username = jwtTokenUtil .getUsernameFromToken (jwtToken );
} catch (IllegalArgumentException e ) {
logger .error ("JWT_TOKEN_UNABLE_TO_GET_USERNAME" , e );
} catch (ExpiredJwtException e ) {
logger .warn ("JWT_TOKEN_EXPIRED" , e );
}
} else {
logger .warn ("JWT_TOKEN_DOES_NOT_START_WITH_BEARER_STRING" );
}
logger .debug ("JWT_TOKEN_USERNAME_VALUE '{}'" , username );
if (username != null && SecurityContextHolder .getContext ().getAuthentication () == null ) {
UserDetails userDetails = this .jwtInMemoryUserDetailsService .loadUserByUsername (username );
if (jwtTokenUtil .validateToken (jwtToken , userDetails )) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken (userDetails , null , userDetails .getAuthorities ());
usernamePasswordAuthenticationToken .setDetails (new WebAuthenticationDetailsSource ().buildDetails (request ));
SecurityContextHolder .getContext ().setAuthentication (usernamePasswordAuthenticationToken );
}
}
chain .doFilter (request , response );
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/JwtTokenUtil.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt ;
import java .io .Serializable ;
import java .util .Date ;
import java .util .HashMap ;
import java .util .Map ;
import java .util .function .Function ;
import org .springframework .beans .factory .annotation .Value ;
import org .springframework .security .core .userdetails .UserDetails ;
import org .springframework .stereotype .Component ;
import io .jsonwebtoken .Claims ;
import io .jsonwebtoken .Clock ;
import io .jsonwebtoken .Jwts ;
import io .jsonwebtoken .SignatureAlgorithm ;
import io .jsonwebtoken .impl .DefaultClock ;
@ Component
public class JwtTokenUtil implements Serializable {
static final String CLAIM_KEY_USERNAME = "sub" ;
static final String CLAIM_KEY_CREATED = "iat" ;
private static final long serialVersionUID = -3301605591108950415L ;
private Clock clock = DefaultClock .INSTANCE ;
@ Value ("${jwt.signing.key.secret}" )
private String secret ;
@ Value ("${jwt.token.expiration.in.seconds}" )
private Long expiration ;
public String getUsernameFromToken (String token ) {
return getClaimFromToken (token , Claims ::getSubject );
}
public Date getIssuedAtDateFromToken (String token ) {
return getClaimFromToken (token , Claims ::getIssuedAt );
}
public Date getExpirationDateFromToken (String token ) {
return getClaimFromToken (token , Claims ::getExpiration );
}
public <T > T getClaimFromToken (String token , Function <Claims , T > claimsResolver ) {
final Claims claims = getAllClaimsFromToken (token );
return claimsResolver .apply (claims );
}
private Claims getAllClaimsFromToken (String token ) {
return Jwts .parser ().setSigningKey (secret ).parseClaimsJws (token ).getBody ();
}
private Boolean isTokenExpired (String token ) {
final Date expiration = getExpirationDateFromToken (token );
return expiration .before (clock .now ());
}
private Boolean ignoreTokenExpiration (String token ) {
// here you specify tokens, for that the expiration is ignored
return false ;
}
public String generateToken (UserDetails userDetails ) {
Map <String , Object > claims = new HashMap <>();
return doGenerateToken (claims , userDetails .getUsername ());
}
private String doGenerateToken (Map <String , Object > claims , String subject ) {
final Date createdDate = clock .now ();
final Date expirationDate = calculateExpirationDate (createdDate );
return Jwts .builder ().setClaims (claims ).setSubject (subject ).setIssuedAt (createdDate )
.setExpiration (expirationDate ).signWith (SignatureAlgorithm .HS512 , secret ).compact ();
}
public Boolean canTokenBeRefreshed (String token ) {
return (!isTokenExpired (token ) || ignoreTokenExpiration (token ));
}
public String refreshToken (String token ) {
final Date createdDate = clock .now ();
final Date expirationDate = calculateExpirationDate (createdDate );
final Claims claims = getAllClaimsFromToken (token );
claims .setIssuedAt (createdDate );
claims .setExpiration (expirationDate );
return Jwts .builder ().setClaims (claims ).signWith (SignatureAlgorithm .HS512 , secret ).compact ();
}
public Boolean validateToken (String token , UserDetails userDetails ) {
JwtUserDetails user = (JwtUserDetails ) userDetails ;
final String username = getUsernameFromToken (token );
return (username .equals (user .getUsername ()) && !isTokenExpired (token ));
}
private Date calculateExpirationDate (Date createdDate ) {
return new Date (createdDate .getTime () + expiration * 1000 );
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/JwtUnAuthorizedResponseAuthenticationEntryPoint.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt ;
import java .io .IOException ;
import java .io .Serializable ;
import javax .servlet .http .HttpServletRequest ;
import javax .servlet .http .HttpServletResponse ;
import org .springframework .security .core .AuthenticationException ;
import org .springframework .security .web .AuthenticationEntryPoint ;
import org .springframework .stereotype .Component ;
@ Component
public class JwtUnAuthorizedResponseAuthenticationEntryPoint implements AuthenticationEntryPoint , Serializable {
private static final long serialVersionUID = -8970718410437077606L ;
@ Override
public void commence (HttpServletRequest request , HttpServletResponse response ,
AuthenticationException authException ) throws IOException {
response .sendError (HttpServletResponse .SC_UNAUTHORIZED ,
"You would need to provide the Jwt Token to Access This resource" );
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/JwtUserDetails.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt ;
import java .util .ArrayList ;
import java .util .Collection ;
import java .util .List ;
import org .springframework .security .core .GrantedAuthority ;
import org .springframework .security .core .authority .SimpleGrantedAuthority ;
import org .springframework .security .core .userdetails .UserDetails ;
import com .fasterxml .jackson .annotation .JsonIgnore ;
public class JwtUserDetails implements UserDetails {
private static final long serialVersionUID = 5155720064139820502L ;
private final Long id ;
private final String username ;
private final String password ;
private final Collection <? extends GrantedAuthority > authorities ;
public JwtUserDetails (Long id , String username , String password , String role ) {
this .id = id ;
this .username = username ;
this .password = password ;
List <SimpleGrantedAuthority > authorities = new ArrayList <SimpleGrantedAuthority >();
authorities .add (new SimpleGrantedAuthority (role ));
this .authorities = authorities ;
}
@ JsonIgnore
public Long getId () {
return id ;
}
@ Override
public String getUsername () {
return username ;
}
@ JsonIgnore
@ Override
public boolean isAccountNonExpired () {
return true ;
}
@ JsonIgnore
@ Override
public boolean isAccountNonLocked () {
return true ;
}
@ JsonIgnore
@ Override
public boolean isCredentialsNonExpired () {
return true ;
}
@ JsonIgnore
@ Override
public String getPassword () {
return password ;
}
@ Override
public Collection <? extends GrantedAuthority > getAuthorities () {
return authorities ;
}
@ Override
public boolean isEnabled () {
return true ;
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/JwtUserDetailsService.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .security .core .userdetails .UserDetails ;
import org .springframework .security .core .userdetails .UserDetailsService ;
import org .springframework .security .core .userdetails .UsernameNotFoundException ;
import org .springframework .stereotype .Service ;
@ Service
public class JwtUserDetailsService implements UserDetailsService {
@ Autowired
private UserRepository userRepository ;
@ Override
public UserDetails loadUserByUsername (String username ) throws UsernameNotFoundException {
User user = userRepository .findByUsername (username );
if (user == null ) {
throw new UsernameNotFoundException (String .format ("USER_NOT_FOUND '%s'." , username ));
} else {
return create (user );
}
}
public static JwtUserDetails create (User user ) {
return new JwtUserDetails (user .getId (), user .getUsername (), user .getPassword (), user .getRole ());
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/User.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt ;
import javax .persistence .Column ;
import javax .persistence .Entity ;
import javax .persistence .GeneratedValue ;
import javax .persistence .GenerationType ;
import javax .persistence .Id ;
import javax .persistence .SequenceGenerator ;
import javax .persistence .Table ;
import javax .validation .constraints .NotNull ;
import javax .validation .constraints .Size ;
@ Entity
@ Table (name = "USER" )
public class User {
@ Id
@ Column (name = "ID" )
@ GeneratedValue (strategy = GenerationType .SEQUENCE , generator = "user_seq" )
@ SequenceGenerator (name = "user_seq" , sequenceName = "user_seq" , allocationSize = 1 )
private Long id ;
@ Column (name = "USERNAME" , length = 50 , unique = true )
@ NotNull
@ Size (min = 4 , max = 50 )
private String username ;
@ Column (name = "PASSWORD" , length = 100 )
@ Size (min = 4 , max = 100 )
@ NotNull
private String password ;
@ Column (name = "ROLE" , length = 100 )
@ Size (min = 4 , max = 100 )
@ NotNull
private String role ;
public Long getId () {
return id ;
}
public void setId (Long id ) {
this .id = id ;
}
public String getUsername () {
return username ;
}
public void setUsername (String username ) {
this .username = username ;
}
public String getPassword () {
return password ;
}
public void setPassword (String password ) {
this .password = password ;
}
public String getRole () {
return role ;
}
public void setRole (String role ) {
this .role = role ;
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/UserRepository.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt ;
import org .springframework .data .jpa .repository .JpaRepository ;
public interface UserRepository extends JpaRepository <User , Long > {
User findByUsername (String username );
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/resource/AuthenticationException.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt .resource ;
public class AuthenticationException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 5978387939943664344L ;
public AuthenticationException (String message , Throwable cause ) {
super (message , cause );
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/resource/JwtAuthenticationRestController.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt .resource ;
import java .util .Objects ;
import javax .servlet .http .HttpServletRequest ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .beans .factory .annotation .Value ;
import org .springframework .http .HttpStatus ;
import org .springframework .http .ResponseEntity ;
import org .springframework .security .authentication .AuthenticationManager ;
import org .springframework .security .authentication .BadCredentialsException ;
import org .springframework .security .authentication .DisabledException ;
import org .springframework .security .authentication .UsernamePasswordAuthenticationToken ;
import org .springframework .security .core .userdetails .UserDetails ;
import org .springframework .security .core .userdetails .UserDetailsService ;
import org .springframework .web .bind .annotation .ExceptionHandler ;
import org .springframework .web .bind .annotation .RequestBody ;
import org .springframework .web .bind .annotation .RequestMapping ;
import org .springframework .web .bind .annotation .RequestMethod ;
import org .springframework .web .bind .annotation .RestController ;
import com .in28minutes .rest .webservices .restfulwebservices .jwt .JwtTokenUtil ;
@ RestController
//@CrossOrigin(origins="http://localhost:4200")Replace with global config
public class JwtAuthenticationRestController {
@ Value ("${jwt.http.request.header}" )
private String tokenHeader ;
@ Autowired
private AuthenticationManager authenticationManager ;
@ Autowired
private JwtTokenUtil jwtTokenUtil ;
@ Autowired
private UserDetailsService jwtInMemoryUserDetailsService ;
@ RequestMapping (value = "${jwt.get.token.uri}" , method = RequestMethod .POST )
public ResponseEntity <?> createAuthenticationToken (@ RequestBody JwtTokenRequest authenticationRequest )
throws AuthenticationException {
authenticate (authenticationRequest .getUsername (), authenticationRequest .getPassword ());
final UserDetails userDetails = jwtInMemoryUserDetailsService .loadUserByUsername (authenticationRequest .getUsername ());
final String token = jwtTokenUtil .generateToken (userDetails );
return ResponseEntity .ok (new JwtTokenResponse (token ));
}
@ RequestMapping (value = "${jwt.refresh.token.uri}" , method = RequestMethod .GET )
public ResponseEntity <?> refreshAndGetAuthenticationToken (HttpServletRequest request ) {
String authToken = request .getHeader (tokenHeader );
final String token = authToken .substring (7 );
String username = jwtTokenUtil .getUsernameFromToken (token );
jwtInMemoryUserDetailsService .loadUserByUsername (username );
if (jwtTokenUtil .canTokenBeRefreshed (token )) {
String refreshedToken = jwtTokenUtil .refreshToken (token );
return ResponseEntity .ok (new JwtTokenResponse (refreshedToken ));
} else {
return ResponseEntity .badRequest ().body (null );
}
}
@ ExceptionHandler ({ AuthenticationException .class })
public ResponseEntity <String > handleAuthenticationException (AuthenticationException e ) {
return ResponseEntity .status (HttpStatus .UNAUTHORIZED ).body (e .getMessage ());
}
private void authenticate (String username , String password ) {
Objects .requireNonNull (username );
Objects .requireNonNull (password );
try {
authenticationManager .authenticate (new UsernamePasswordAuthenticationToken (username , password ));
} catch (DisabledException e ) {
throw new AuthenticationException ("USER_DISABLED" , e );
} catch (BadCredentialsException e ) {
throw new AuthenticationException ("INVALID_CREDENTIALS" , e );
}
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/resource/JwtTokenRequest.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt .resource ;
import java .io .Serializable ;
public class JwtTokenRequest implements Serializable {
private static final long serialVersionUID = -5616176897013108345L ;
private String username ;
private String password ;
public JwtTokenRequest () {
super ();
}
public JwtTokenRequest (String username , String password ) {
this .setUsername (username );
this .setPassword (password );
}
public String getUsername () {
return this .username ;
}
public void setUsername (String username ) {
this .username = username ;
}
public String getPassword () {
return this .password ;
}
public void setPassword (String password ) {
this .password = password ;
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/jwt/resource/JwtTokenResponse.java
package com .in28minutes .rest .webservices .restfulwebservices .jwt .resource ;
import java .io .Serializable ;
public class JwtTokenResponse implements Serializable {
private static final long serialVersionUID = 8317676219297719109L ;
private final String token ;
public JwtTokenResponse (String token ) {
this .token = token ;
}
public String getToken () {
return this .token ;
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/todo/Todo.java
package com .in28minutes .rest .webservices .restfulwebservices .todo ;
import java .util .Date ;
import javax .persistence .Entity ;
import javax .persistence .GeneratedValue ;
import javax .persistence .Id ;
@ Entity
public class Todo {
@ Id
@ GeneratedValue
private Long id ;
private String username ;
private String description ;
private Date targetDate ;
private boolean isDone ;
public Todo () {
}
public Todo (long id , String username , String description , Date targetDate , boolean isDone ) {
super ();
this .id = id ;
this .username = username ;
this .description = description ;
this .targetDate = targetDate ;
this .isDone = isDone ;
}
public Long getId () {
return id ;
}
public void setId (Long id ) {
this .id = id ;
}
public String getUsername () {
return username ;
}
public void setUsername (String username ) {
this .username = username ;
}
public String getDescription () {
return description ;
}
public void setDescription (String description ) {
this .description = description ;
}
public Date getTargetDate () {
return targetDate ;
}
public void setTargetDate (Date targetDate ) {
this .targetDate = targetDate ;
}
public boolean isDone () {
return isDone ;
}
public void setDone (boolean isDone ) {
this .isDone = isDone ;
}
@ Override
public int hashCode () {
final int prime = 31 ;
int result = 1 ;
result = prime * result + (int ) (id ^ (id >>> 32 ));
return result ;
}
@ Override
public boolean equals (Object obj ) {
if (this == obj )
return true ;
if (obj == null )
return false ;
if (getClass () != obj .getClass ())
return false ;
Todo other = (Todo ) obj ;
if (id != other .id )
return false ;
return true ;
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/todo/TodoJpaRepository.java
package com .in28minutes .rest .webservices .restfulwebservices .todo ;
import java .util .List ;
import org .springframework .data .jpa .repository .JpaRepository ;
import org .springframework .stereotype .Repository ;
@ Repository
public interface TodoJpaRepository extends JpaRepository <Todo , Long >{
List <Todo > findByUsername (String username );
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/java/com/in28minutes/rest/webservices/restfulwebservices/todo/TodoJpaResource.java
package com .in28minutes .rest .webservices .restfulwebservices .todo ;
import java .net .URI ;
import java .util .List ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .http .HttpStatus ;
import org .springframework .http .ResponseEntity ;
import org .springframework .web .bind .annotation .DeleteMapping ;
import org .springframework .web .bind .annotation .GetMapping ;
import org .springframework .web .bind .annotation .PathVariable ;
import org .springframework .web .bind .annotation .PostMapping ;
import org .springframework .web .bind .annotation .PutMapping ;
import org .springframework .web .bind .annotation .RequestBody ;
import org .springframework .web .bind .annotation .RestController ;
import org .springframework .web .servlet .support .ServletUriComponentsBuilder ;
@ RestController
public class TodoJpaResource {
@ Autowired
private TodoJpaRepository todoJpaRepository ;
@ GetMapping ("/jpa/users/{username}/todos" )
public List <Todo > getAllTodos (@ PathVariable String username ){
return todoJpaRepository .findByUsername (username );
//return todoService.findAll();
}
@ GetMapping ("/jpa/users/{username}/todos/{id}" )
public Todo getTodo (@ PathVariable String username , @ PathVariable long id ){
return todoJpaRepository .findById (id ).get ();
//return todoService.findById(id);
}
// DELETE /users/{username}/todos/{id}
@ DeleteMapping ("/jpa/users/{username}/todos/{id}" )
public ResponseEntity <Void > deleteTodo (
@ PathVariable String username , @ PathVariable long id ) {
todoJpaRepository .deleteById (id );
return ResponseEntity .noContent ().build ();
}
//Edit/Update a Todo
//PUT /users/{user_name}/todos/{todo_id}
@ PutMapping ("/jpa/users/{username}/todos/{id}" )
public ResponseEntity <Todo > updateTodo (
@ PathVariable String username ,
@ PathVariable long id , @ RequestBody Todo todo ){
todo .setUsername (username );
Todo todoUpdated = todoJpaRepository .save (todo );
return new ResponseEntity <Todo >(todoUpdated , HttpStatus .OK );
}
@ PostMapping ("/jpa/users/{username}/todos" )
public ResponseEntity <Void > createTodo (
@ PathVariable String username , @ RequestBody Todo todo ){
todo .setUsername (username );
Todo createdTodo = todoJpaRepository .save (todo );
//Location
//Get current resource url
///{id}
URI uri = ServletUriComponentsBuilder .fromCurrentRequest ()
.path ("/{id}" ).buildAndExpand (createdTodo .getId ()).toUri ();
return ResponseEntity .created (uri ).build ();
}
}
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/resources/application.properties
logging.level.org.springframework = info
# spring.security.user.name=in28minutes
# spring.security.user.password=dummy
jwt.signing.key.secret =mySecret
jwt.get.token.uri =/authenticate
jwt.refresh.token.uri =/refresh
jwt.http.request.header =Authorization
jwt.token.expiration.in.seconds =604800
spring.jpa.show-sql =true
spring.h2.console.enabled =true
server.port = 8080
# eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJpbjI4bWludXRlcyIsImV4cCI6MTU2Mjg0ODAwMCwiaWF0IjoxNTYyMjQzMjAwfQ.Nmix4DqNSB13ufoy845GUETluidrp9afwafdTVtfKxr4UkUYb75mfjLHY2rD7BVDEqREoCtguTnoV3ISrdn6Eg
/04-spring-boot-react-full-stack-h2/restful-web-services/src/main/resources/data.sql
/*https://www.browserling.com/tools/bcrypt Use Rounds 10*/
/*in28minutes/dummy*/
INSERT INTO USER (ID, USERNAME, PASSWORD, ROLE)
VALUES (1, 'in28minutes', '$2a$10$3zHzb.Npv1hfZbLEU5qsdOju/tk2je6W6PnNnY.c1ujWPcZh4PL6e','ROLE_USER');
/*in28minutes2/mypassword*/
INSERT INTO USER (ID, USERNAME, PASSWORD, ROLE)
VALUES (2, 'in28minutes2', '$2a$10$i9AckmxMkb4yKtLCdxeQheCm2pXWB3qZ2G189/Ph/DUci1DvLO.Rq','ROLE_USER');
insert into todo(id, username,description,target_date,is_done)
values(10001, 'in28minutes', 'Learn JPA', sysdate(), false);
insert into todo(id, username,description,target_date,is_done)
values(10002, 'in28minutes', 'Learn Data JPA', sysdate(), false);
insert into todo(id, username,description,target_date,is_done)
values(10003, 'in28minutes', 'Learn Microservices', sysdate(), false);
/04-spring-boot-react-full-stack-h2/restful-web-services/src/test/java/com/in28minutes/rest/webservices/restfulwebservices/RestfulWebServicesApplicationTests.java
package com .in28minutes .rest .webservices .restfulwebservices ;
import org .junit .Test ;
import org .junit .runner .RunWith ;
import org .springframework .boot .test .context .SpringBootTest ;
import org .springframework .test .context .junit4 .SpringRunner ;
@ RunWith (SpringRunner .class )
@ SpringBootTest
public class RestfulWebServicesApplicationTests {
@ Test
public void contextLoads () {
}
}
/05-simple-microservices/currency-conversion-service/Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
EXPOSE 8100
ADD target/*.jar app.jar
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
/05-simple-microservices/currency-conversion-service/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.in28minutes.microservices</groupId >
<artifactId >06-currency-conversion-service</artifactId >
<version >0.0.1-SNAPSHOT</version >
<packaging >jar</packaging >
<name >currency-conversion-service</name >
<description >Demo project for Spring Boot</description >
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-parent</artifactId >
<version >2.1.7.RELEASE</version >
<relativePath /> <!-- lookup parent from repository -->
</parent >
<properties >
<project .build.sourceEncoding>UTF-8</project .build.sourceEncoding>
<project .reporting.outputEncoding>UTF-8</project .reporting.outputEncoding>
<java .version>1.8</java .version>
<maven-jar-plugin .version>3.1.1</maven-jar-plugin .version>
<spring-cloud .version>Greenwich.RELEASE</spring-cloud .version>
</properties >
<dependencies >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-web</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.cloud</groupId >
<artifactId >spring-cloud-starter-openfeign</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-data-jpa</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-security</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.cloud</groupId >
<artifactId >spring-cloud-starter-sleuth</artifactId >
</dependency >
<!-- <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
-->
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-actuator</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-devtools</artifactId >
<scope >runtime</scope >
</dependency >
<dependency >
<groupId >com.h2database</groupId >
<artifactId >h2</artifactId >
<scope >runtime</scope >
</dependency >
<dependency >
<groupId >javax.xml.bind</groupId >
<artifactId >jaxb-api</artifactId >
</dependency >
<dependency >
<groupId >com.sun.xml.bind</groupId >
<artifactId >jaxb-impl</artifactId >
<version >2.3.1</version >
</dependency >
<dependency >
<groupId >org.glassfish.jaxb</groupId >
<artifactId >jaxb-runtime</artifactId >
</dependency >
<dependency >
<groupId >javax.activation</groupId >
<artifactId >activation</artifactId >
<version >1.1.1</version >
</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 >
<finalName >currency-conversion-service</finalName >
<plugins >
<plugin >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-maven-plugin</artifactId >
</plugin >
<!-- Docker -->
<plugin >
<groupId >com.spotify</groupId >
<artifactId >dockerfile-maven-plugin</artifactId >
<version >1.4.10</version >
<executions >
<execution >
<id >default</id >
<goals >
<goal >build</goal >
<!-- <goal>push</goal> -->
</goals >
</execution >
</executions >
<configuration >
<repository >in28min/${project.name}</repository >
<tag >${project.version}</tag >
<skipDockerInfo >true</skipDockerInfo >
</configuration >
</plugin >
</plugins >
</build >
<repositories >
<repository >
<id >spring-snapshots</id >
<name >Spring Snapshots</name >
<url >https://repo.spring.io/snapshot</url >
<snapshots >
<enabled >true</enabled >
</snapshots >
</repository >
<repository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestone</url >
<snapshots >
<enabled >false</enabled >
</snapshots >
</repository >
</repositories >
<pluginRepositories >
<pluginRepository >
<id >spring-snapshots</id >
<name >Spring Snapshots</name >
<url >https://repo.spring.io/snapshot</url >
<snapshots >
<enabled >true</enabled >
</snapshots >
</pluginRepository >
<pluginRepository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestone</url >
<snapshots >
<enabled >false</enabled >
</snapshots >
</pluginRepository >
</pluginRepositories >
</project >
/05-simple-microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java
package com .in28minutes .microservices .currencyconversionservice ;
import org .springframework .boot .SpringApplication ;
import org .springframework .boot .autoconfigure .SpringBootApplication ;
import org .springframework .cloud .openfeign .EnableFeignClients ;
@ SpringBootApplication
@ EnableFeignClients ("com.in28minutes.microservices.currencyconversionservice.resource" )
//@EnableDiscoveryClient
public class CurrencyConversionServiceApplication {
public static void main (String [] args ) {
SpringApplication .run (CurrencyConversionServiceApplication .class , args );
}
}
/05-simple-microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/resource/CurrencyConversionBean.java
package com .in28minutes .microservices .currencyconversionservice .resource ;
import java .math .BigDecimal ;
public class CurrencyConversionBean {
private Long id ;
private String from ;
private String to ;
private BigDecimal conversionMultiple ;
private BigDecimal quantity ;
private BigDecimal totalCalculatedAmount ;
public CurrencyConversionBean () {
}
public CurrencyConversionBean (Long id , String from , String to , BigDecimal conversionMultiple , BigDecimal quantity ,
BigDecimal totalCalculatedAmount ) {
super ();
this .id = id ;
this .from = from ;
this .to = to ;
this .conversionMultiple = conversionMultiple ;
this .quantity = quantity ;
this .totalCalculatedAmount = totalCalculatedAmount ;
}
public Long getId () {
return id ;
}
public void setId (Long id ) {
this .id = id ;
}
public String getFrom () {
return from ;
}
public void setFrom (String from ) {
this .from = from ;
}
public String getTo () {
return to ;
}
public void setTo (String to ) {
this .to = to ;
}
public BigDecimal getConversionMultiple () {
return conversionMultiple ;
}
public void setConversionMultiple (BigDecimal conversionMultiple ) {
this .conversionMultiple = conversionMultiple ;
}
public BigDecimal getQuantity () {
return quantity ;
}
public void setQuantity (BigDecimal quantity ) {
this .quantity = quantity ;
}
public BigDecimal getTotalCalculatedAmount () {
return totalCalculatedAmount ;
}
public void setTotalCalculatedAmount (BigDecimal totalCalculatedAmount ) {
this .totalCalculatedAmount = totalCalculatedAmount ;
}
}
/05-simple-microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/resource/CurrencyConversionController.java
package com .in28minutes .microservices .currencyconversionservice .resource ;
import java .math .BigDecimal ;
import org .slf4j .Logger ;
import org .slf4j .LoggerFactory ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .web .bind .annotation .GetMapping ;
import org .springframework .web .bind .annotation .PathVariable ;
import org .springframework .web .bind .annotation .RestController ;
@ RestController
public class CurrencyConversionController {
private static final Logger LOGGER = LoggerFactory .getLogger (CurrencyConversionController .class );
@ Autowired
private CurrencyExchangeServiceProxy proxy ;
@ GetMapping ("/currency-converter/from/{from}/to/{to}/quantity/{quantity}" )
public CurrencyConversionBean convertCurrency (@ PathVariable String from , @ PathVariable String to ,
@ PathVariable BigDecimal quantity ) {
LOGGER .info ("Received Request to convert from {} {} to {}. " , quantity , from , to );
CurrencyConversionBean response = proxy .retrieveExchangeValue (from , to );
BigDecimal convertedValue = quantity .multiply (response .getConversionMultiple ());
return new CurrencyConversionBean (response .getId (), from , to , response .getConversionMultiple (), quantity ,
convertedValue );
}
}
/05-simple-microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/resource/CurrencyExchangeServiceProxy.java
package com .in28minutes .microservices .currencyconversionservice .resource ;
import org .springframework .cloud .openfeign .FeignClient ;
import org .springframework .web .bind .annotation .GetMapping ;
import org .springframework .web .bind .annotation .PathVariable ;
////http://localhost:8000
//@FeignClient(name = "currency-exchange-service", url="${CURRENCY_EXCHANGE_URI:http://localhost:8000}")
@ FeignClient (name ="netflix-zuul-api-gateway-server" )
public interface CurrencyExchangeServiceProxy {
/// currency-exchange/from/EUR/to/INR
//@GetMapping("/currency-exchange/from/{from}/to/{to}")
@ GetMapping ("/currency-exchange-service/currency-exchange/from/{from}/to/{to}" )
public CurrencyConversionBean retrieveExchangeValue (@ PathVariable ("from" ) String from ,
@ PathVariable ("to" ) String to );
}
/05-simple-microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/SecurityConfiguration.java
package com .in28minutes .microservices .currencyconversionservice .security ;
import org .springframework .context .annotation .Configuration ;
import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
import org .springframework .security .config .annotation .web .configuration .WebSecurityConfigurerAdapter ;
@ Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@ Override
protected void configure (HttpSecurity http ) throws Exception {
http
.authorizeRequests ().anyRequest ().permitAll ()
.and ()
.httpBasic ().disable ()
.csrf ().disable ();
}
}
/05-simple-microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/util/environment/EnvironmentConfigurationLogger.java
package com .in28minutes .microservices .currencyconversionservice .util .environment ;
import java .util .Arrays ;
import java .util .stream .StreamSupport ;
import org .slf4j .Logger ;
import org .slf4j .LoggerFactory ;
import org .springframework .context .event .ContextRefreshedEvent ;
import org .springframework .context .event .EventListener ;
import org .springframework .core .env .AbstractEnvironment ;
import org .springframework .core .env .EnumerablePropertySource ;
import org .springframework .core .env .Environment ;
import org .springframework .core .env .MutablePropertySources ;
import org .springframework .stereotype .Component ;
@ Component
public class EnvironmentConfigurationLogger {
private static final Logger LOGGER = LoggerFactory .getLogger (EnvironmentConfigurationLogger .class );
@ SuppressWarnings ("rawtypes" )
@ EventListener
public void handleContextRefresh (ContextRefreshedEvent event ) {
final Environment environment = event .getApplicationContext ().getEnvironment ();
LOGGER .info ("====== Environment and configuration ======" );
LOGGER .info ("Active profiles: {}" , Arrays .toString (environment .getActiveProfiles ()));
final MutablePropertySources sources = ((AbstractEnvironment ) environment ).getPropertySources ();
StreamSupport .stream (sources .spliterator (), false ).filter (ps -> ps instanceof EnumerablePropertySource )
.map (ps -> ((EnumerablePropertySource ) ps ).getPropertyNames ()).flatMap (Arrays ::stream ).distinct ()
.forEach (prop -> {
LOGGER .info ("{}" , prop );
// Object resolved = environment.getProperty(prop, Object.class);
// if (resolved instanceof String) {
// LOGGER.info("{}", environment.getProperty(prop));
// }
});
LOGGER .info ("===========================================" );
}
}
/05-simple-microservices/currency-conversion-service/src/main/resources/application.properties
spring.application.name =currency-conversion-service
server.port =8100
management.endpoints.web.base-path =/manage
management.endpoints.web.exposure.include =*
# logging.level.org.springframework=debug
spring.security.user.name =in28minutes
spring.security.user.password =dummy
# Eureka
# eureka.client.service-url.default-zone=http://localhost:8761/eureka
# eureka.client.service-url.defaultZone=http://naming-server:8761/eureka/
# RabbitMQ
# spring.rabbitmq.host=rabbitmq
# spring.rabbitmq.addresses=rabbitmq:5672
spring.sleuth.sampler.probability =1.0
# Feign and Ribbon Timeouts
feign.client.config.default.connectTimeout =50000
feign.client.config.default.readTimeout =50000
ribbon.ConnectTimeout = 60000
ribbon.ReadTimeout = 60000
/05-simple-microservices/currency-conversion-service/src/test/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplicationTests.java
package com .in28minutes .microservices .currencyconversionservice ;
import org .junit .Test ;
import org .junit .runner .RunWith ;
import org .springframework .boot .test .context .SpringBootTest ;
import org .springframework .test .context .junit4 .SpringRunner ;
@ RunWith (SpringRunner .class )
@ SpringBootTest
public class CurrencyConversionServiceApplicationTests {
@ Test
public void contextLoads () {
}
}
/05-simple-microservices/currency-exchange-service/Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
EXPOSE 8000
ADD target/*.jar app.jar
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
/05-simple-microservices/currency-exchange-service/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.in28minutes.microservices</groupId >
<artifactId >05-currency-exchange-service</artifactId >
<version >0.0.1-SNAPSHOT</version >
<packaging >jar</packaging >
<name >currency-exchange-service</name >
<description >Demo project for Spring Boot</description >
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-parent</artifactId >
<version >2.1.1.RELEASE</version >
<relativePath /> <!-- lookup parent from repository -->
</parent >
<properties >
<project .build.sourceEncoding>UTF-8</project .build.sourceEncoding>
<project .reporting.outputEncoding>UTF-8</project .reporting.outputEncoding>
<java .version>1.8</java .version>
<maven-jar-plugin .version>3.1.1</maven-jar-plugin .version>
<spring-cloud .version>Greenwich.RC2</spring-cloud .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-data-jpa</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-security</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.cloud</groupId >
<artifactId >spring-cloud-starter-sleuth</artifactId >
</dependency >
<!-- <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
-->
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-actuator</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-devtools</artifactId >
<scope >runtime</scope >
</dependency >
<dependency >
<groupId >com.h2database</groupId >
<artifactId >h2</artifactId >
<scope >runtime</scope >
</dependency >
<dependency >
<groupId >javax.xml.bind</groupId >
<artifactId >jaxb-api</artifactId >
</dependency >
<dependency >
<groupId >com.sun.xml.bind</groupId >
<artifactId >jaxb-impl</artifactId >
<version >2.3.1</version >
</dependency >
<dependency >
<groupId >org.glassfish.jaxb</groupId >
<artifactId >jaxb-runtime</artifactId >
</dependency >
<dependency >
<groupId >javax.activation</groupId >
<artifactId >activation</artifactId >
<version >1.1.1</version >
</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 >
<finalName >currency-exchange-service</finalName >
<plugins >
<plugin >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-maven-plugin</artifactId >
</plugin >
<!-- Docker -->
<plugin >
<groupId >com.spotify</groupId >
<artifactId >dockerfile-maven-plugin</artifactId >
<version >1.4.10</version >
<executions >
<execution >
<id >default</id >
<goals >
<goal >build</goal >
<!-- <goal>push</goal> -->
</goals >
</execution >
</executions >
<configuration >
<repository >in28min/${project.name}</repository >
<tag >${project.version}</tag >
<skipDockerInfo >true</skipDockerInfo >
</configuration >
</plugin >
</plugins >
</build >
<repositories >
<repository >
<id >spring-snapshots</id >
<name >Spring Snapshots</name >
<url >https://repo.spring.io/snapshot</url >
<snapshots >
<enabled >true</enabled >
</snapshots >
</repository >
<repository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestone</url >
<snapshots >
<enabled >false</enabled >
</snapshots >
</repository >
</repositories >
<pluginRepositories >
<pluginRepository >
<id >spring-snapshots</id >
<name >Spring Snapshots</name >
<url >https://repo.spring.io/snapshot</url >
<snapshots >
<enabled >true</enabled >
</snapshots >
</pluginRepository >
<pluginRepository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestone</url >
<snapshots >
<enabled >false</enabled >
</snapshots >
</pluginRepository >
</pluginRepositories >
</project >
/05-simple-microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeServiceApplicationH2.java
package com .in28minutes .microservices .currencyexchangeservice ;
import org .springframework .boot .SpringApplication ;
import org .springframework .boot .autoconfigure .SpringBootApplication ;
@ SpringBootApplication
//@EnableDiscoveryClient
public class CurrencyExchangeServiceApplicationH2 {
public static void main (String [] args ) {
SpringApplication .run (CurrencyExchangeServiceApplicationH2 .class , args );
}
}
/05-simple-microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/resource/CurrencyExchangeController.java
package com .in28minutes .microservices .currencyexchangeservice .resource ;
import java .util .Map ;
import org .slf4j .Logger ;
import org .slf4j .LoggerFactory ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .web .bind .annotation .GetMapping ;
import org .springframework .web .bind .annotation .PathVariable ;
import org .springframework .web .bind .annotation .RequestHeader ;
import org .springframework .web .bind .annotation .RestController ;
@ RestController
public class CurrencyExchangeController {
private static final Logger LOGGER = LoggerFactory .getLogger (CurrencyExchangeController .class );
@ Autowired
private ExchangeValueRepository repository ;
@ GetMapping ("/currency-exchange/from/{from}/to/{to}" )
public ExchangeValue retrieveExchangeValue (@ PathVariable String from , @ PathVariable String to ,
@ RequestHeader Map <String , String > headers ) {
printAllHeaders (headers );
ExchangeValue exchangeValue = repository .findByFromAndTo (from , to );
LOGGER .info ("{} {} {}" , from , to , exchangeValue );
if (exchangeValue == null ) {
throw new RuntimeException ("Unable to find data to convert " + from + " to " + to );
}
return exchangeValue ;
}
private void printAllHeaders (Map <String , String > headers ) {
headers .forEach ((key , value ) -> {
LOGGER .info (String .format ("Header '%s' = %s" , key , value ));
});
}
}
/05-simple-microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/resource/ExchangeValue.java
package com .in28minutes .microservices .currencyexchangeservice .resource ;
import java .math .BigDecimal ;
import javax .persistence .Column ;
import javax .persistence .Entity ;
import javax .persistence .Id ;
import javax .persistence .Transient ;
@ Entity
public class ExchangeValue {
@ Id
private Long id ;
@ Column (name = "currency_from" )
private String from ;
@ Column (name = "currency_to" )
private String to ;
private BigDecimal conversionMultiple ;
public ExchangeValue () {
}
public ExchangeValue (Long id , String from , String to , BigDecimal conversionMultiple ) {
super ();
this .id = id ;
this .from = from ;
this .to = to ;
this .conversionMultiple = conversionMultiple ;
}
public Long getId () {
return id ;
}
public String getFrom () {
return from ;
}
public String getTo () {
return to ;
}
public BigDecimal getConversionMultiple () {
return conversionMultiple ;
}
}
/05-simple-microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/resource/ExchangeValueRepository.java
package com .in28minutes .microservices .currencyexchangeservice .resource ;
import org .springframework .data .jpa .repository .JpaRepository ;
public interface ExchangeValueRepository extends JpaRepository <ExchangeValue , Long > {
ExchangeValue findByFromAndTo (String from , String to );
}
/05-simple-microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/security/SecurityConfiguration.java
package com .in28minutes .microservices .currencyexchangeservice .security ;
import org .springframework .context .annotation .Configuration ;
import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
import org .springframework .security .config .annotation .web .configuration .WebSecurityConfigurerAdapter ;
@ Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@ Override
protected void configure (HttpSecurity http ) throws Exception {
http
.authorizeRequests ().anyRequest ().permitAll ()
.and ()
.httpBasic ().disable ()
.csrf ().disable ();
}
}
/05-simple-microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/util/environment/EnvironmentConfigurationLogger.java
package com .in28minutes .microservices .currencyexchangeservice .util .environment ;
import java .util .Arrays ;
import java .util .stream .StreamSupport ;
import org .slf4j .Logger ;
import org .slf4j .LoggerFactory ;
import org .springframework .context .event .ContextRefreshedEvent ;
import org .springframework .context .event .EventListener ;
import org .springframework .core .env .AbstractEnvironment ;
import org .springframework .core .env .EnumerablePropertySource ;
import org .springframework .core .env .Environment ;
import org .springframework .core .env .MutablePropertySources ;
import org .springframework .stereotype .Component ;
@ Component
public class EnvironmentConfigurationLogger {
private static final Logger LOGGER = LoggerFactory .getLogger (EnvironmentConfigurationLogger .class );
@ SuppressWarnings ("rawtypes" )
@ EventListener
public void handleContextRefresh (ContextRefreshedEvent event ) {
final Environment environment = event .getApplicationContext ().getEnvironment ();
LOGGER .info ("====== Environment and configuration ======" );
LOGGER .info ("Active profiles: {}" , Arrays .toString (environment .getActiveProfiles ()));
final MutablePropertySources sources = ((AbstractEnvironment ) environment ).getPropertySources ();
StreamSupport .stream (sources .spliterator (), false ).filter (ps -> ps instanceof EnumerablePropertySource )
.map (ps -> ((EnumerablePropertySource ) ps ).getPropertyNames ()).flatMap (Arrays ::stream ).distinct ()
.forEach (prop -> {
LOGGER .info ("{}" , prop );
// Object resolved = environment.getProperty(prop, Object.class);
// if (resolved instanceof String) {
// LOGGER.info("{}", environment.getProperty(prop));
// }
});
LOGGER .info ("===========================================" );
}
}
/05-simple-microservices/currency-exchange-service/src/main/resources/application.properties
spring.application.name =currency-exchange-service
server.port =8000
spring.jpa.show-sql =true
spring.h2.console.enabled =true
spring.h2.console.settings.web-allow-others =true
# logging.level.org.springframework=debug
management.endpoints.web.base-path =/manage
management.endpoints.web.exposure.include =*
spring.security.user.name =in28minutes
spring.security.user.password =dummy
# Eureka
# eureka.client.service-url.default-zone=http://localhost:8761/eureka
# eureka.client.service-url.defaultZone=http://naming-server:8761/eureka/
# Zipkin
# spring.zipkin.base-url=http://zipkin-server:9411/
# RabbitMQ
# spring.rabbitmq.host=rabbitmq
# spring.rabbitmq.addresses=rabbitmq:5672
spring.sleuth.sampler.probability =1.0
# Feign and Ribbon Timeouts
feign.client.config.default.connectTimeout =50000
feign.client.config.default.readTimeout =50000
ribbon.ConnectTimeout = 60000
ribbon.ReadTimeout = 60000
/05-simple-microservices/currency-exchange-service/src/main/resources/data.sql
insert into exchange_value(id,currency_from,currency_to,conversion_multiple)
values(10001,'USD','INR',65);
insert into exchange_value(id,currency_from,currency_to,conversion_multiple)
values(10002,'EUR','INR',75);
insert into exchange_value(id,currency_from,currency_to,conversion_multiple)
values(10003,'AUD','INR',25);
/05-simple-microservices/currency-exchange-service/src/test/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeServiceApplicationTests.java
package com .in28minutes .microservices .currencyexchangeservice ;
import org .junit .Test ;
import org .junit .runner .RunWith ;
import org .springframework .boot .test .context .SpringBootTest ;
import org .springframework .test .context .junit4 .SpringRunner ;
@ RunWith (SpringRunner .class )
@ SpringBootTest
public class CurrencyExchangeServiceApplicationTests {
@ Test
public void contextLoads () {
}
}
/05-simple-microservices/docker-compose-starting.yml
version: '3.7'
# Removed subprocess.CalledProcessError: Command '['/usr/local/bin/docker-credential-desktop', 'get']' returned non-zero exit status 1
# I had this:
# cat ~/.docker/config.json
# {"auths":{},"credsStore":"", "credsStore":"desktop","stackOrchestrator":"swarm"}
# I updated to this:
# {"auths":{},"credsStore":"","stackOrchestrator":"swarm"}
services:
currency-exchange-service:
#image: in28min/todo-web-application-mysql:0.0.1-SNAPSHOT
build:
context: currency-exchange-service
#context: .
#dockerfile: Dockerfile
ports:
- "8000:8000"
restart: always
#depends_on: # Start the depends_on first
#- todo-api
environment:
BACKEND_API_BASE_URL: http://localhost:8080
RDS_PORT: 3306
networks:
- currency-network
currency-conversion-service:
#image: in28min/todo-web-application-mysql:0.0.1-SNAPSHOT
build:
context: currency-conversion-service
#context: .
#dockerfile: Dockerfile
ports:
- "8100:8100"
restart: always
depends_on: # Start the depends_on first
- currency-exchange-service
environment:
CURRENCY_EXCHANGE_URI: http://currency-exchange-service:8000
networks:
- currency-network
# Networks to be created to facilitate communication between containers
networks:
currency-network:
/05-simple-microservices/docker-compose.yml
version: '3.7'
# Removed subprocess.CalledProcessError: Command '['/usr/local/bin/docker-credential-desktop', 'get']' returned non-zero exit status 1
# I had this:
# cat ~/.docker/config.json
# {"auths":{},"credsStore":"", "credsStore":"desktop","stackOrchestrator":"swarm"}
# I updated to this:
# {"auths":{},"credsStore":"","stackOrchestrator":"swarm"}
services:
rabbitmq:
container_name: rabbitmq
image: rabbitmq:3.5.3-management
ports:
- "5672:5672"
- "15672:15672"
networks:
- currency-microservices-network
naming-server:
build: netflix-eureka-naming-server
container_name: naming-server
ports:
- "8761:8761"
- "28787:8787"
networks:
- currency-microservices-network
zipkin-server:
image: openzipkin/zipkin
container_name: zipkin
environment:
- STORAGE_TYPE=mem
# Uncomment to enable debug logging
# - JAVA_OPTS=-Dlogging.level.zipkin=DEBUG
depends_on: # Start the depends_on first
- rabbitmq
environment:
- RABBIT_URI=amqp://guest:guest@rabbitmq:5672
ports:
- 9411:9411
networks:
- currency-microservices-network
currency-exchange-service:
image: in28min/currency-exchange-service:0.0.1-SNAPSHOT
container_name: currency-exchange-service
#build:
#context: ../05-simple-microservices/currency-exchange-service
#context: .
#dockerfile: Dockerfile
ports:
- "8000:8000"
restart: always
depends_on: # Start the depends_on first
- rabbitmq
- zipkin-server
- naming-server
- zuul-api-gateway
environment:
- RABBIT_URI=amqp://guest:guest@rabbitmq:5672
networks:
- currency-microservices-network
currency-conversion-service:
image: in28min/currency-conversion-service:0.0.1-SNAPSHOT
container_name: currency-conversion-service
#build:
#context: ../05-simple-microservices/currency-conversion-service
#context: .
#dockerfile: Dockerfile
ports:
- "8100:8100"
restart: always
depends_on: # Start the depends_on first
- rabbitmq
- zipkin-server
- currency-exchange-service
- naming-server
- zuul-api-gateway
environment:
- CURRENCY_EXCHANGE_URI=http://currency-exchange-service:8000
- RABBIT_URI=amqp://guest:guest@rabbitmq:5672
networks:
- currency-microservices-network
zuul-api-gateway:
image: in28min/netflix-zuul-api-gateway-server:0.0.1-SNAPSHOT
container_name: zuul-api-gateway
#build:
#context: ../05-simple-microservices/currency-conversion-service
#context: .
#dockerfile: Dockerfile
ports:
- "8765:8765"
restart: always
depends_on: # Start the depends_on first
- rabbitmq
- zipkin-server
- naming-server
environment:
- RABBIT_URI=amqp://guest:guest@rabbitmq:5672
networks:
- currency-microservices-network
# Networks to be created to facilitate communication between containers
networks:
currency-microservices-network:
/05-simple-microservices/netflix-eureka-naming-server/Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
EXPOSE 8761
ADD target/*.jar app.jar
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
/05-simple-microservices/netflix-eureka-naming-server/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.in28minutes.microservices</groupId >
<artifactId >07-netflix-eureka-naming-server</artifactId >
<version >0.0.1-SNAPSHOT</version >
<packaging >jar</packaging >
<name >netflix-eureka-naming-server</name >
<description >Demo project for Spring Boot</description >
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-parent</artifactId >
<version >2.0.0.RELEASE</version >
<relativePath /> <!-- lookup parent from repository -->
</parent >
<properties >
<project .build.sourceEncoding>UTF-8</project .build.sourceEncoding>
<project .reporting.outputEncoding>UTF-8</project .reporting.outputEncoding>
<java .version>1.8</java .version>
<spring-cloud .version>Finchley.M8</spring-cloud .version>
</properties >
<dependencies >
<dependency >
<groupId >org.springframework.cloud</groupId >
<artifactId >spring-cloud-starter-netflix-eureka-server</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-actuator</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-devtools</artifactId >
<scope >runtime</scope >
</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 >
<plugin >
<groupId >com.spotify</groupId >
<artifactId >dockerfile-maven-plugin</artifactId >
<version >1.4.10</version >
<executions >
<execution >
<id >default</id >
<goals >
<goal >build</goal >
<!-- <goal>push</goal> -->
</goals >
</execution >
</executions >
<configuration >
<repository >in28min/${project.name}</repository >
<tag >${project.version}</tag >
<skipDockerInfo >true</skipDockerInfo >
</configuration >
</plugin >
</plugins >
</build >
<repositories >
<repository >
<id >spring-snapshots</id >
<name >Spring Snapshots</name >
<url >https://repo.spring.io/snapshot</url >
<snapshots >
<enabled >true</enabled >
</snapshots >
</repository >
<repository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestone</url >
<snapshots >
<enabled >false</enabled >
</snapshots >
</repository >
</repositories >
<pluginRepositories >
<pluginRepository >
<id >spring-snapshots</id >
<name >Spring Snapshots</name >
<url >https://repo.spring.io/snapshot</url >
<snapshots >
<enabled >true</enabled >
</snapshots >
</pluginRepository >
<pluginRepository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestone</url >
<snapshots >
<enabled >false</enabled >
</snapshots >
</pluginRepository >
</pluginRepositories >
</project >
/05-simple-microservices/netflix-eureka-naming-server/src/main/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplication.java
package com .in28minutes .microservices .netflixeurekanamingserver ;
import org .springframework .boot .SpringApplication ;
import org .springframework .boot .autoconfigure .SpringBootApplication ;
import org .springframework .cloud .netflix .eureka .server .EnableEurekaServer ;
@ SpringBootApplication
@ EnableEurekaServer
public class NetflixEurekaNamingServerApplication {
public static void main (String [] args ) {
SpringApplication .run (NetflixEurekaNamingServerApplication .class , args );
}
}
/05-simple-microservices/netflix-eureka-naming-server/src/main/resources/application.properties
spring.application.name =netflix-eureka-naming-server
server.port =8761
eureka.client.register-with-eureka =false
eureka.client.fetch-registry =false
/05-simple-microservices/netflix-eureka-naming-server/src/test/java/com/in28minutes/microservices/netflixeurekanamingserver/NetflixEurekaNamingServerApplicationTests.java
package com .in28minutes .microservices .netflixeurekanamingserver ;
import org .junit .Test ;
import org .junit .runner .RunWith ;
import org .springframework .boot .test .context .SpringBootTest ;
import org .springframework .test .context .junit4 .SpringRunner ;
@ RunWith (SpringRunner .class )
@ SpringBootTest
public class NetflixEurekaNamingServerApplicationTests {
@ Test
public void contextLoads () {
}
}
/05-simple-microservices/netflix-zuul-api-gateway-server/Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
EXPOSE 8765
ADD target/*.jar app.jar
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
/05-simple-microservices/netflix-zuul-api-gateway-server/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.in28minutes.microservices</groupId >
<artifactId >08-netflix-zuul-api-gateway-server</artifactId >
<version >0.0.1-SNAPSHOT</version >
<packaging >jar</packaging >
<name >netflix-zuul-api-gateway-server</name >
<description >Demo project for Spring Boot</description >
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-parent</artifactId >
<version >2.1.1.RELEASE</version >
<relativePath /> <!-- lookup parent from repository -->
</parent >
<properties >
<project .build.sourceEncoding>UTF-8</project .build.sourceEncoding>
<project .reporting.outputEncoding>UTF-8</project .reporting.outputEncoding>
<java .version>1.8</java .version>
<spring-cloud .version>Greenwich.RC2</spring-cloud .version>
</properties >
<dependencies >
<dependency >
<groupId >org.springframework.cloud</groupId >
<artifactId >spring-cloud-starter-netflix-zuul</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.cloud</groupId >
<artifactId >spring-cloud-starter-sleuth</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.cloud</groupId >
<artifactId >spring-cloud-starter-netflix-eureka-client</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.cloud</groupId >
<artifactId >spring-cloud-starter-zipkin</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.amqp</groupId >
<artifactId >spring-rabbit</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-actuator</artifactId >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-devtools</artifactId >
<scope >runtime</scope >
</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 >
<finalName >zuul-api-gateway</finalName >
<plugins >
<plugin >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-maven-plugin</artifactId >
</plugin >
<!-- Docker -->
<plugin >
<groupId >com.spotify</groupId >
<artifactId >dockerfile-maven-plugin</artifactId >
<version >1.4.10</version >
<executions >
<execution >
<id >default</id >
<goals >
<goal >build</goal >
<!-- <goal>push</goal> -->
</goals >
</execution >
</executions >
<configuration >
<repository >in28min/${project.name}</repository >
<tag >${project.version}</tag >
<skipDockerInfo >true</skipDockerInfo >
</configuration >
</plugin >
</plugins >
</build >
<repositories >
<repository >
<id >spring-snapshots</id >
<name >Spring Snapshots</name >
<url >https://repo.spring.io/snapshot</url >
<snapshots >
<enabled >true</enabled >
</snapshots >
</repository >
<repository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestone</url >
<snapshots >
<enabled >false</enabled >
</snapshots >
</repository >
</repositories >
<pluginRepositories >
<pluginRepository >
<id >spring-snapshots</id >
<name >Spring Snapshots</name >
<url >https://repo.spring.io/snapshot</url >
<snapshots >
<enabled >true</enabled >
</snapshots >
</pluginRepository >
<pluginRepository >
<id >spring-milestones</id >
<name >Spring Milestones</name >
<url >https://repo.spring.io/milestone</url >
<snapshots >
<enabled >false</enabled >
</snapshots >
</pluginRepository >
</pluginRepositories >
</project >
/05-simple-microservices/netflix-zuul-api-gateway-server/src/main/java/com/in28minutes/microservices/netflixzuulapigatewayserver/NetflixZuulApiGatewayServerApplication.java
package com .in28minutes .microservices .netflixzuulapigatewayserver ;
import org .springframework .boot .SpringApplication ;
import org .springframework .boot .autoconfigure .SpringBootApplication ;
import org .springframework .cloud .client .discovery .EnableDiscoveryClient ;
import org .springframework .cloud .netflix .zuul .EnableZuulProxy ;
import org .springframework .context .annotation .Bean ;
import brave .sampler .Sampler ;
@ EnableZuulProxy
@ EnableDiscoveryClient
@ SpringBootApplication
public class NetflixZuulApiGatewayServerApplication {
public static void main (String [] args ) {
SpringApplication .run (NetflixZuulApiGatewayServerApplication .class , args );
}
@ Bean
public Sampler defaultSampler (){
return Sampler .ALWAYS_SAMPLE ;
}
}
/05-simple-microservices/netflix-zuul-api-gateway-server/src/main/java/com/in28minutes/microservices/netflixzuulapigatewayserver/ZuulLoggingFilter.java
package com .in28minutes .microservices .netflixzuulapigatewayserver ;
import javax .servlet .http .HttpServletRequest ;
import org .slf4j .Logger ;
import org .slf4j .LoggerFactory ;
import org .springframework .stereotype .Component ;
import com .netflix .zuul .ZuulFilter ;
import com .netflix .zuul .context .RequestContext ;
@ Component
public class ZuulLoggingFilter extends ZuulFilter {
private Logger logger = LoggerFactory .getLogger (this .getClass ());
@ Override
public boolean shouldFilter () {
return true ;
}
@ Override
public Object run () {
HttpServletRequest request =
RequestContext .getCurrentContext ().getRequest ();
logger .info ("request -> {} request uri -> {}" ,
request , request .getRequestURI ());
return null ;
}
@ Override
public String filterType () {
return "pre" ;
}
@ Override
public int filterOrder () {
return 1 ;
}
}
/05-simple-microservices/netflix-zuul-api-gateway-server/src/main/resources/application.properties
spring.application.name =netflix-zuul-api-gateway-server
server.port =8765
management.endpoints.web.base-path =/manage
management.endpoints.web.exposure.include =*
# logging.level.org.springframework=debug
# Eureka
# eureka.client.service-url.default-zone=http://localhost:8761/eureka
eureka.client.service-url.defaultZone =http://naming-server:8761/eureka/
# Zipkin
# spring.zipkin.base-url=http://zipkin-server:9411/
# RabbitMQ
spring.rabbitmq.host =rabbitmq
spring.sleuth.sampler.probability =1.0
# Feign and Ribbon Timeouts
feign.client.config.default.connectTimeout =50000
feign.client.config.default.readTimeout =50000
ribbon.ConnectTimeout = 60000
ribbon.ReadTimeout = 60000
/05-simple-microservices/netflix-zuul-api-gateway-server/src/test/java/com/in28minutes/microservices/netflixzuulapigatewayserver/NetflixZuulApiGatewayServerApplicationTests.java
package com .in28minutes .microservices .netflixzuulapigatewayserver ;
import org .junit .Test ;
import org .junit .runner .RunWith ;
import org .springframework .boot .test .context .SpringBootTest ;
import org .springframework .test .context .junit4 .SpringRunner ;
@ RunWith (SpringRunner .class )
@ SpringBootTest
public class NetflixZuulApiGatewayServerApplicationTests {
@ Test
public void contextLoads () {
}
}