MoinMoin Logo
  • Comments
  • Immutable Page
  • Menu
    • Navigation
    • RecentChanges
    • FindPage
    • Local Site Map
    • Help
    • HelpContents
    • HelpOnMoinWikiSyntax
    • Display
    • Attachments
    • Info
    • Raw Text
    • Print View
    • Edit
    • Load
    • Save
  • Login

Navigation

  • Start
  • Sitemap
Revision 23 as of 2016-09-29 18:07:02
  • Java
  • Spring
  • SpringBoot

SpringBoot

Example

pom.xml

   1 <?xml version="1.0" encoding="UTF-8"?>
   2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   3     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   4     <modelVersion>4.0.0</modelVersion>
   5     <groupId>hello</groupId>
   6     <artifactId>test-spring-boot</artifactId>
   7     <version>0.1.0</version>
   8     <parent>
   9         <groupId>org.springframework.boot</groupId>
  10         <artifactId>spring-boot-starter-parent</artifactId>
  11         <version>1.4.0.RELEASE</version>
  12     </parent>
  13     <dependencies>
  14         <dependency>
  15             <groupId>org.springframework.boot</groupId>
  16             <artifactId>spring-boot-starter-thymeleaf</artifactId>
  17         </dependency>
  18         <dependency>
  19             <groupId>org.mariadb.jdbc</groupId>
  20             <artifactId>mariadb-java-client</artifactId>
  21             <version>1.4.4</version>
  22         </dependency>
  23         <dependency>
  24             <groupId>commons-dbcp</groupId>
  25             <artifactId>commons-dbcp</artifactId>
  26             <version>1.4</version>
  27         </dependency>
  28 
  29          <dependency>
  30             <groupId>org.springframework</groupId>
  31             <artifactId>spring-jdbc</artifactId>
  32             <version>4.3.2.RELEASE</version>
  33         </dependency>
  34 
  35     </dependencies>
  36     <properties>
  37         <start-class>hello.Application</start-class>
  38     </properties>
  39     <build>
  40         <plugins>
  41             <plugin>
  42                 <groupId>org.springframework.boot</groupId>
  43                 <artifactId>spring-boot-maven-plugin</artifactId>
  44             </plugin>
  45         </plugins>
  46     </build>
  47     <repositories>
  48         <repository>
  49             <id>spring-milestone</id>
  50             <url>http://repo.spring.io/libs-release</url>
  51         </repository>
  52     </repositories>
  53     <pluginRepositories>
  54         <pluginRepository>
  55             <id>spring-milestone</id>
  56             <url>http://repo.spring.io/libs-release</url>
  57         </pluginRepository>
  58     </pluginRepositories>
  59 </project>

src/main/java/hello/GreetingController.java

   1 package hello;
   2 
   3 import org.springframework.stereotype.Controller;
   4 import org.springframework.ui.Model;
   5 import org.springframework.web.bind.annotation.RequestMapping;
   6 import org.springframework.web.bind.annotation.RequestParam;
   7 
   8 import org.slf4j.Logger;
   9 import org.slf4j.LoggerFactory;
  10 import org.springframework.web.bind.annotation.ResponseBody;
  11 import org.springframework.beans.factory.annotation.Autowired;
  12 
  13 import java.util.List;
  14 import java.util.ArrayList;
  15 
  16 @Controller
  17 public class GreetingController {
  18     private final Logger logger = LoggerFactory.getLogger(GreetingController.class);
  19     @Autowired
  20     DummyDAO dummyDAO;
  21 
  22     public GreetingController(){
  23         logger.debug("Greeting controller created.");
  24     }
  25 
  26     @RequestMapping("/greeting")
  27     // http://localhost:8080/greeting?name=nnnn 
  28     public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
  29         logger.info("Greeting endpoint called.");
  30         model.addAttribute("name", name);
  31         return "greeting";
  32     }
  33 
  34     @RequestMapping(value="/dummy",produces="application/json")
  35     @ResponseBody
  36     // http://localhost:8080/dummy
  37     public List<Dummy> dummy(){
  38       List<Dummy> list= new java.util.ArrayList<Dummy>();
  39       Dummy dummy = new Dummy();
  40       dummy.setFieldA("AAA");
  41       dummy.setFieldB("CCC");
  42       list.add(dummy);
  43 
  44       Dummy dummy2 = new Dummy();
  45       dummy2.setFieldA("AAA2");
  46       dummy2.setFieldB("CCC2");
  47       list.add(dummy2);
  48 
  49       return list;
  50     }
  51 
  52     @RequestMapping(value="/dummyname",produces="application/json")
  53     @ResponseBody
  54     // http://localhost:8080/dummyname
  55     public String getDummyName(){
  56       return dummyDAO.getNameFromDummy();
  57     }
  58 
  59 
  60 }

src/main/java/hello/Application.java

   1 package hello;
   2 
   3 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
   4 import org.springframework.boot.SpringApplication;
   5 import org.springframework.context.annotation.ComponentScan;
   6 import org.springframework.context.annotation.Bean;
   7 import org.springframework.beans.factory.annotation.Autowired;
   8 import org.springframework.beans.factory.annotation.Qualifier;
   9 
  10 import org.apache.commons.dbcp.BasicDataSource;
  11 import org.springframework.jdbc.core.JdbcTemplate;
  12 
  13 import org.slf4j.Logger;
  14 import org.slf4j.LoggerFactory;
  15 
  16 @ComponentScan
  17 @EnableAutoConfiguration
  18 public class Application {
  19     private static Logger logger;
  20 
  21     public static void main(String[] args) {
  22         logger = LoggerFactory.getLogger(Application.class);
  23         logger.info("Starting application");
  24         SpringApplication.run(Application.class, args);
  25     }
  26 
  27     @Bean(name="basicDataSource",destroyMethod="close")
  28     public BasicDataSource createBasicDataSource(){
  29       logger.info("Creating basicDataSource");
  30       BasicDataSource bds = new BasicDataSource();
  31       bds.setDriverClassName("org.mariadb.jdbc.Driver");
  32       bds.setUrl("jdbc:mariadb://localhost:3306/springmvchtml");
  33       bds.setUsername("usertest");
  34       bds.setPassword("usertest");
  35       bds.setInitialSize(3);
  36       return bds;
  37     }
  38 /*
  39     @Bean(name="jdbcTemplate")
  40     public JdbcTemplate jdbcTemplate(){
  41       logger.info("Creating jdbcTemplate");
  42       return new JdbcTemplate( basicDataSource() );
  43     }
  44 */
  45     @Bean(name="jdbcTemplate")
  46     public JdbcTemplate createJdbcTemplate(@Autowired @Qualifier("basicDataSource") BasicDataSource bds  ){
  47       logger.info("Creating jdbcTemplate");
  48       return new JdbcTemplate( bds );
  49     }
  50 
  51 }

src/main/java/hello/ThreadTimer.java

   1 package hello;
   2 
   3 import org.springframework.stereotype.Component;
   4 import javax.annotation.PostConstruct;
   5 import org.springframework.beans.factory.annotation.Autowired;
   6 import java.text.MessageFormat;
   7 import org.slf4j.Logger;
   8 import org.slf4j.LoggerFactory;
   9 import java.util.ArrayList;
  10 
  11 @Component
  12 public class ThreadTimer extends Thread {
  13   private int delaySeconds;
  14   private Logger logger;
  15   private boolean running;
  16   private Object monitor=new Object();
  17   private ArrayList<Object> subscribers;
  18 
  19   //@Autowired
  20   //WaitThread waitThread;
  21 
  22   public ThreadTimer() {
  23     this.logger = LoggerFactory.getLogger(ThreadTimer.class);
  24     logger.info("Created instance of " + this.getClass().getSimpleName());
  25     this.running = true;
  26     this.delaySeconds = 5 * 1000;
  27     this.setName(this.getClass().getSimpleName() + "_" + this.getName());
  28     this.subscribers = new ArrayList<Object>();
  29   }
  30 
  31   public void addSubscriber(Object subscriber){
  32     this.subscribers.add(subscriber);
  33   }
  34 
  35   @PostConstruct
  36   public void init() {
  37     logger.info("Starting the thread");
  38     this.start();
  39   }
  40 
  41   @Override
  42   public void run() {
  43     while (running) {
  44       try {
  45         Thread.sleep(this.delaySeconds);
  46         logger.info("Delay " + this.getClass().getSimpleName());
  47 
  48         for(Object o: this.subscribers){
  49           synchronized(o){
  50             o.notify();
  51           }
  52         }
  53       }
  54       catch (InterruptedException e) {
  55           logger.info("ThreadTimer interrupted exception:" + e.getMessage() );
  56       }
  57       catch (Exception e) {
  58           e.printStackTrace();
  59           logger.info("ThreadTimer exception:" + e.getMessage() );
  60           stopRunning();
  61       }
  62     }
  63     logger.info("Exited " + this.getClass().getSimpleName());
  64   }
  65 
  66   public void startRunning() {
  67     this.running = true;
  68   }
  69 
  70   public void stopRunning() {
  71     this.running = false;
  72   }
  73 
  74   public void destroy(){
  75     logger.info("Called destroy");
  76     this.stopRunning();
  77     this.interrupt();
  78   }
  79 }

src/main/java/hello/WaitThread.java

   1 package hello;
   2 
   3 import org.springframework.stereotype.Component;
   4 import javax.annotation.PostConstruct;
   5 import java.text.MessageFormat;
   6 import org.slf4j.Logger;
   7 import org.slf4j.LoggerFactory;
   8 import org.springframework.beans.factory.annotation.Autowired;
   9 
  10 @Component
  11 public class WaitThread extends Thread {
  12   private Logger logger;
  13   private boolean running;
  14   private Object monitor;
  15 
  16   @Autowired
  17   ThreadTimer timerThread;
  18 
  19   public WaitThread() {
  20     this.logger = LoggerFactory.getLogger(WaitThread.class);
  21     logger.info("Created instance of " + this.getClass().getSimpleName());
  22     this.running = true;
  23     this.setName(this.getClass().getSimpleName() + "_" + this.getName());
  24     this.monitor=new Object();
  25   }
  26 
  27   @PostConstruct
  28   public void init() {
  29     this.timerThread.addSubscriber(this);
  30     logger.info("Starting the thread");
  31     this.start();
  32   }
  33 
  34   public void run() {
  35     while (running) {
  36       try {
  37         synchronized(this){
  38           this.wait();
  39           logger.info("Notification received.");
  40         }
  41       }
  42       catch (InterruptedException e) {
  43           logger.info("ThreadTimer interrupted exception:" + e.getMessage() );
  44       }
  45       catch (Exception e) {
  46           logger.info("WaitThread exception:" + e.getMessage() );
  47           stopRunning();
  48       }
  49     }
  50     logger.info("Exited " + this.getClass().getSimpleName());
  51   }
  52 
  53   public void startRunning() {
  54     this.running = true;
  55   }
  56 
  57   public void stopRunning() {
  58     this.running = false;
  59   }
  60 
  61   public void destroy(){
  62     logger.info("Called destroy");
  63     this.stopRunning();
  64     this.interrupt();
  65   }
  66 }

src/main/java/hello/Dummy.java

   1 package hello;
   2 
   3 public class Dummy{
   4   private String fieldA;
   5   private String fieldB;
   6 
   7   public Dummy(){
   8   }
   9 
  10   public String getFieldA(){
  11     return fieldA;
  12   }
  13 
  14   public String getFieldB(){
  15     return fieldB;
  16   }
  17 
  18   public void setFieldA(String arg){
  19     fieldA = arg;
  20   }
  21 
  22   public void setFieldB(String arg){
  23     fieldB = arg;
  24   }
  25 }

src/main/java/hello/DummyDAO.java

   1 package hello;
   2 
   3 import org.slf4j.Logger;
   4 import org.slf4j.LoggerFactory;
   5 
   6 import org.springframework.beans.factory.annotation.Autowired;
   7 import org.springframework.beans.factory.annotation.Qualifier;
   8 import org.springframework.jdbc.core.JdbcTemplate;
   9 import org.springframework.stereotype.Component;
  10 
  11 @Component
  12 public class DummyDAO {
  13   @Autowired
  14   @Qualifier("jdbcTemplate")
  15   private JdbcTemplate jdbcTemplate;
  16   private Logger logger;
  17 
  18   public DummyDAO() {
  19     logger = LoggerFactory.getLogger(DummyDAO.class);
  20     logger.info("Created " + this.getClass().getSimpleName());
  21   }
  22 
  23   public String getNameFromDummy() {
  24     return this.jdbcTemplate.queryForObject("select name from dummy limit 1", String.class);
  25   }
  26 }

src/main/resources/templates/greeting.html

   1 <!DOCTYPE HTML>
   2 <html xmlns:th="http://www.thymeleaf.org">
   3 <head>
   4     <title>Getting Started: Serving Web Content</title>
   5     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
   6 </head>
   7 <body>
   8     <p th:text="'Hello, ' + ${name} + '!'" />
   9 </body>
  10 </html>

src/main/resources/application.properties

   1 logging.file=/tmp/testout.log

src/main/resources/logback-spring.xml

   1 <?xml version="1.0" encoding="UTF-8"?>
   2 <configuration>
   3 <!--
   4     <include resource="org/springframework/boot/logging/logback/defaults.xml" />
   5     <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
   6     <include resource="org/springframework/boot/logging/logback/file-appender.xml" />
   7 -->
   8     <!-- override spring logback default behaviour -->
   9     <appender name="FILE" class="ch.qos.logback.core.FileAppender">
  10         <file>${filelog}</file>
  11         <encoder>
  12                 <pattern>%date{ISO8601} [%thread] %-5level %logger{35} - %msg%n</pattern>
  13         </encoder>
  14     </appender>
  15 
  16     <appender name="GREETFILE" class="ch.qos.logback.core.FileAppender">
  17         <file>/tmp/greet.log</file>
  18         <encoder>
  19                 <pattern>%date{ISO8601} [%thread] %-5level %logger{35} - %msg%n</pattern>
  20         </encoder>
  21     </appender>
  22 
  23     <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
  24         <layout class="ch.qos.logback.classic.PatternLayout">
  25                 <Pattern>%yellow(%date{ISO8601}) %green([%thread]) %highlight(%-5level) %cyan(%logger{35}) - %white(%msg%n) </Pattern>
  26         </layout>
  27     </appender>
  28 
  29     <root level="INFO">
  30         <appender-ref ref="FILE" />
  31         <appender-ref ref="CONSOLE"/>
  32     </root>
  33 
  34     <logger name="hello.GreetingController" level="debug" additivity="false">
  35         <appender-ref ref="GREETFILE"/>
  36         <appender-ref ref="CONSOLE" />
  37     </logger>
  38 
  39 </configuration>

src/main/resources/test.sql

   1 --JDBC URL: jdbc:mariadb://localhost:3306/springmvchtml
   2 --mysql -u root -p
   3 create database springmvchtml;
   4 create user 'usertest'@'%' identified by '????????';
   5 create user 'usertest'@'localhost' identified by '????????';
   6 grant all on springmvchtml.* to 'usertest'@'%';
   7 grant all on springmvchtml.* to 'usertest'@'localhost';
   8 show grants for 'usertest'@'%';
   9 show grants for 'usertest'@'localhost';
  10 create table springmvchtml.dummy (name varchar(255) ) ;
  11 insert into springmvchtml.dummy (name) values('aaaa');
  12 insert into springmvchtml.dummy (name) values('bbbb');
  13 commit;
  14 -- mysql -u usertest -p
  15 

JAR files list

  • jar tf target/test-spring-boot-0.1.0.jar | grep "\.jar"

BOOT-INF/lib/jackson-core-2.8.1.jar
BOOT-INF/lib/spring-web-4.3.2.RELEASE.jar
BOOT-INF/lib/unbescape-1.1.0.RELEASE.jar
BOOT-INF/lib/javassist-3.20.0-GA.jar
BOOT-INF/lib/mariadb-java-client-1.4.4.jar
BOOT-INF/lib/jul-to-slf4j-1.7.21.jar
BOOT-INF/lib/groovy-2.4.7.jar
BOOT-INF/lib/hibernate-validator-5.2.4.Final.jar
BOOT-INF/lib/classmate-1.3.1.jar
BOOT-INF/lib/tomcat-embed-el-8.5.4.jar
BOOT-INF/lib/tomcat-embed-core-8.5.4.jar
BOOT-INF/lib/commons-pool-1.6.jar
BOOT-INF/lib/thymeleaf-spring4-2.1.5.RELEASE.jar
BOOT-INF/lib/spring-context-4.3.2.RELEASE.jar
BOOT-INF/lib/thymeleaf-2.1.5.RELEASE.jar
BOOT-INF/lib/spring-boot-starter-tomcat-1.4.0.RELEASE.jar
BOOT-INF/lib/thymeleaf-layout-dialect-1.4.0.jar
BOOT-INF/lib/spring-boot-autoconfigure-1.4.0.RELEASE.jar
BOOT-INF/lib/snakeyaml-1.17.jar
BOOT-INF/lib/spring-boot-starter-web-1.4.0.RELEASE.jar
BOOT-INF/lib/spring-boot-starter-logging-1.4.0.RELEASE.jar
BOOT-INF/lib/spring-webmvc-4.3.2.RELEASE.jar
BOOT-INF/lib/spring-beans-4.3.2.RELEASE.jar
BOOT-INF/lib/slf4j-api-1.7.21.jar
BOOT-INF/lib/spring-jdbc-4.3.2.RELEASE.jar
BOOT-INF/lib/jackson-annotations-2.8.1.jar
BOOT-INF/lib/commons-dbcp-1.4.jar
BOOT-INF/lib/ognl-3.0.8.jar
BOOT-INF/lib/spring-boot-starter-1.4.0.RELEASE.jar
BOOT-INF/lib/tomcat-embed-websocket-8.5.4.jar
BOOT-INF/lib/spring-boot-starter-thymeleaf-1.4.0.RELEASE.jar
BOOT-INF/lib/spring-tx-4.3.2.RELEASE.jar
BOOT-INF/lib/logback-classic-1.1.7.jar
BOOT-INF/lib/spring-boot-1.4.0.RELEASE.jar
BOOT-INF/lib/spring-expression-4.3.2.RELEASE.jar
BOOT-INF/lib/logback-core-1.1.7.jar
BOOT-INF/lib/validation-api-1.1.0.Final.jar
BOOT-INF/lib/spring-core-4.3.2.RELEASE.jar
BOOT-INF/lib/jackson-databind-2.8.1.jar
BOOT-INF/lib/jcl-over-slf4j-1.7.21.jar
BOOT-INF/lib/log4j-over-slf4j-1.7.21.jar
BOOT-INF/lib/spring-aop-4.3.2.RELEASE.jar
BOOT-INF/lib/jboss-logging-3.3.0.Final.jar

Add HTTPS/SSL support

Keystore creation

#create keystore with key springboot
keytool -genkey -alias springboot -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore springboot.p12 -validity 3650

Enter keystore password: 12345678
Re-enter new password: 12345678
What is your first and last name?
  [Unknown]:  Spring Boot
What is the name of your organizational unit?
  [Unknown]:  Boot
What is the name of your organization?
  [Unknown]:  Boot
What is the name of your City or Locality?
  [Unknown]:  Lisbon
What is the name of your State or Province?
  [Unknown]:  Estremadura
What is the two-letter country code for this unit?
  [Unknown]:  PT
Is CN=Spring Boot, OU=Boot, O=Boot, L=Lisbon, ST=Estremadura, C=PT correct?
  [no]:  yes

#list keys 
keytool -list -v -keystore springboot.p12 -storetype pkcs12  

Code to add in Application class/configuration

// import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
// import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
// import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
// import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
// import java.io.FileNotFoundException;
// import org.apache.catalina.connector.Connector;
// import org.springframework.beans.factory.annotation.Value;
// import javax.inject.Inject;
// import org.springframework.util.ResourceUtils;

public class TCC implements TomcatConnectorCustomizer{
    private String keystoreFile;
    private String keystorePassword;
    private String keystoreType;
    private String keystoreAlias;

    public TCC(String keystoreFile, String keystorePassword, String keystoreType, String keystoreAlias){
        this.keystoreFile=keystoreFile;
        this.keystorePassword=keystorePassword;
        this.keystoreType=keystoreType;
        this.keystoreAlias=keystoreAlias;
    }

    public void customize(Connector connector){
        connector.setSecure(true);
        connector.setScheme("https");
        connector.setAttribute("keystoreFile", keystoreFile);
        connector.setAttribute("keystorePass", keystorePassword);
        connector.setAttribute("keystoreType", keystoreType);
        connector.setAttribute("keyAlias", keystoreAlias);
        connector.setAttribute("clientAuth", "false");
        connector.setAttribute("sslProtocol", "TLS");
        connector.setAttribute("SSLEnabled", true);
    }
}

public class ESCC implements EmbeddedServletContainerCustomizer{
    private String keystoreFile;
    private String keystorePassword;
    private String keystoreType;
    private String keystoreAlias;

    public ESCC(String keystoreFile, String keystorePassword, String keystoreType, String keystoreAlias){
        this.keystoreFile=keystoreFile;
        this.keystorePassword=keystorePassword;
        this.keystoreType=keystoreType;
        this.keystoreAlias=keystoreAlias;
    }

    public void customize(ConfigurableEmbeddedServletContainer container){
        TomcatEmbeddedServletContainerFactory containerFactory = (TomcatEmbeddedServletContainerFactory) container;

        TomcatConnectorCustomizer tcc = new TCC(keystoreFile,keystorePassword,keystoreType,keystoreAlias);
        containerFactory.addConnectorCustomizers(tcc);
    }
}

@Bean
@Inject
public EmbeddedServletContainerCustomizer containerCustomizer(
    @Value("${keystore.file}") String keystoreFile,
    @Value("${keystore.password}") String keystorePassword,
    @Value("${keystore.type}") String keystoreType,
    @Value("${keystore.alias}") String keystoreAlias) throws FileNotFoundException
{
    final String absoluteKeystoreFile = ResourceUtils.getFile(keystoreFile).getAbsolutePath();
    EmbeddedServletContainerCustomizer escc = new ESCC(absoluteKeystoreFile,keystorePassword,keystoreType,keystoreAlias);
    return escc;
}

pom.xml

<!-- Add dependency to support @Inject annotation -->
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
  • Add server.port=8443 to application.properties or use system property -Dserver.port=8443
  • Run java -Dfilelog=/tmp/out.log -Dkeystore.file=springboot.p12 -Dkeystore.password=12345678 -Dkeystore.type=PKCS12 -Dkeystore.alias=springboot -jar target/test-spring-boot-0.1.0.jar
  • Access https://localhost:8443/greeting?name=x

Environment variables

src/main/resources/application.properties

logging.file=/tmp/testout.log
server.port=8443
foo.bar=bohica

   1 import org.springframework.core.env.Environment;
   2 import org.springframework.beans.factory.annotation.Value;
   3 //....................
   4     @Resource
   5     Environment env;
   6     @Value("${foo.bar}")
   7     private String fooBar;
   8 
   9     @RequestMapping(value="/env",produces="application/json")
  10     @ResponseBody
  11     public List<String> getEnv(){
  12       List<String> ret = new ArrayList<String>();
  13       ret.add( env.getProperty("foo.bar") );
  14       ret.add(fooBar);
  15       return ret;
  16     }
  17 //....................
  18 

Override value in application.properties

  • java -jar target/test-spring-boot-0.1.0.jar --foo.bar=snafu

Instead of using @Autowired or @Inject use @Resource (javax.annotation.Resource).

Example JPA RabbitMQ HttpSession Redis

WaitThread.java

package hello;

import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.text.MessageFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

@Component
public class WaitThread extends Thread {
  private Logger logger;
  private boolean running;
  private Object monitor;

  @Autowired
  ThreadTimer timerThread;

  public WaitThread() {    
    this.logger = LoggerFactory.getLogger(WaitThread.class);
    logger.info("Created instance of " + this.getClass().getSimpleName());
    this.running = true;
    this.setName(this.getClass().getSimpleName() + "_" + this.getName());
    this.monitor=new Object();
  }

  @PostConstruct
  public void init() {
    this.timerThread.addSubscriber(this);
    logger.info("Starting the thread");
    this.start();
  }

  public void run() {
    while (running) {
      try {
        synchronized(this){
          this.wait();
          logger.info("Notification received.");
        }
      }
      catch (InterruptedException e) {
          logger.info("ThreadTimer interrupted exception:" + e.getMessage() );
      }
      catch (Exception e) {
          logger.info("WaitThread exception:" + e.getMessage() );
          stopRunning();
      }      
    }
    logger.info("Exited " + this.getClass().getSimpleName());
  }

  public void startRunning() {
    this.running = true;
  }

  public void stopRunning() {
    this.running = false;
  }
  
  public void destroy(){
    logger.info("Called destroy");
    this.stopRunning();
    this.interrupt();    
  }
}

ThreadTimer.java

package hello;

import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import java.text.MessageFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;

@Component
public class ThreadTimer extends Thread {
  private int delaySeconds;
  private Logger logger;
  private boolean running;
  private Object monitor=new Object();
  private ArrayList<Object> subscribers;

  public ThreadTimer() {    
    this.logger = LoggerFactory.getLogger(ThreadTimer.class);
    logger.info("Created instance of " + this.getClass().getSimpleName());
    this.running = true;
    this.delaySeconds = 5 * 1000;
    this.setName(this.getClass().getSimpleName() + "_" + this.getName());
    this.subscribers = new ArrayList<Object>();
  }

  public void addSubscriber(Object subscriber){
    this.subscribers.add(subscriber);
  }

  @PostConstruct
  public void init() {
    logger.info("Starting the thread");
    this.start();
  }

  @Override
  public void run() {
    while (running) {
      try {
        Thread.sleep(this.delaySeconds);
        logger.info("Delay " + this.getClass().getSimpleName());

        for(Object o: this.subscribers){
          synchronized(o){
            o.notify();
          }
        }
      }
      catch (InterruptedException e) {
          logger.info("ThreadTimer interrupted exception:" + e.getMessage() );
      }
      catch (Exception e) {
          e.printStackTrace();
          logger.info("ThreadTimer exception:" + e.getMessage() );
          stopRunning();
      }      
    }
    logger.info("Exited " + this.getClass().getSimpleName());
  }

  public void startRunning() {
    this.running = true;
  }

  public void stopRunning() {
    this.running = false;
  }
  
  public void destroy(){
    logger.info("Called destroy");
    this.stopRunning();
    this.interrupt();    
  }
}

MessageHandler.java

package hello;

import java.nio.charset.StandardCharsets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MessageHandler{
    private final Logger logger = LoggerFactory.getLogger(MessageHandler.class);
    
    public void handleMessage(byte[] message){
        String msg = new String(message, StandardCharsets.UTF_8);
        logger.info("Received msg " + msg   );
    }

    public void handleMessage(String message){
        logger.info("Received msg " + message   );
    }

}

GreetingController.java

package hello;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.ArrayList;
import org.springframework.web.bind.annotation.PathVariable;
//import javax.inject.Inject;
import org.springframework.core.env.Environment;
import org.springframework.beans.factory.annotation.Value;
import javax.annotation.Resource;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import javax.servlet.http.HttpSession;

@Controller
public class GreetingController {
    private final Logger logger = LoggerFactory.getLogger(GreetingController.class);
    @Resource
    DummyDAO dummyDAO;

    @Resource
    Environment env;

    @Resource
    RabbitTemplate rabbitTemplate;
 
    @Value("${instance.name}")
    String instanceName;
    
    public GreetingController(){
        logger.debug("Greeting controller created.");
    }

    @RequestMapping("/greeting")
    public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model  ) {        
        model.addAttribute("name", name);
        rabbitTemplate.convertAndSend("topicExchange","myQueue", name  );
        rabbitTemplate.convertAndSend("fanoutExchange","", name  );
        return "greeting";
    }

    @RequestMapping("/setvalue/{value}")
    @ResponseBody
    public String setValue(@PathVariable("value") String value, Model model,HttpSession httpSession) {
        httpSession.setAttribute("name", value );
        return httpSession.getId();
    }

    @RequestMapping("/getvalue")
    @ResponseBody
    public String setValue(Model model,HttpSession httpSession) {
        return (String) httpSession.getAttribute("name");        
    }
    
    @RequestMapping("/instancename")
    @ResponseBody
    public String setValue(Model model) {
        return this.instanceName;        
    }
    
    @RequestMapping("/greeting/add/{name}")
    public String addGreeting(@PathVariable("name") String name, Model model) {
        model.addAttribute("name", name);
        dummyDAO.addName(name);
        return "greeting";
    }
    
    @RequestMapping(value="/dummy",produces="application/json")
    @ResponseBody
    public List<DummyJPA> dummy(){
      return dummyDAO.getAllNames();
    }

    @RequestMapping(value="/dummyname",produces="application/json")
    @ResponseBody
    public String getDummyName(){
      return dummyDAO.getNameFromDummy();
    }

    @Value("${foo.bar}")
    private String fooBar;

    @RequestMapping(value="/env",produces="application/json")
    @ResponseBody
    public List<String> getEnv(){
      List<String> ret = new ArrayList<String>(); 
      ret.add( env.getProperty("foo.bar") );
      ret.add(fooBar);
      return ret;
    }
    
}

DummyJPA.java

package hello;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.NamedQueries;

@Entity
@NamedQueries({
    @NamedQuery(name="DummyJPA.getAll",  query="Select e From DummyJPA e")
})
public class DummyJPA{
  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private Long id;
  private String dummy;

  public DummyJPA(){
  } 

  public String getDummy(){
    return this.dummy;
  }

  public void setDummy(String dummy){
    this.dummy=dummy;
  }

  @Override
  public String toString(){
    return String.format("Dummy %s", this.dummy);
  }
}

DummyDAO.java

package hello;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.persistence.PersistenceContext;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.transaction.Transactional;
import java.util.List;

@Component
public class DummyDAO {
  @Autowired
  @Qualifier("jdbcTemplate")
  private JdbcTemplate jdbcTemplate;
  private Logger logger;

  @PersistenceContext(unitName="default")
  EntityManager em;

  public DummyDAO() {
    logger = LoggerFactory.getLogger(DummyDAO.class);
    logger.info("Created " + this.getClass().getSimpleName());
  }

  public String getNameFromDummy() {
    return this.jdbcTemplate.queryForObject("select name from dummy limit 1", String.class);
  }

  @Transactional
  public void addName(String name){
    DummyJPA djpa = new DummyJPA();
    djpa.setDummy(name);
    em.persist(djpa);
  }

  @Transactional
  public List<DummyJPA> getAllNames(){
    return em.createNamedQuery("DummyJPA.getAll",DummyJPA.class).getResultList();
  }

}

Dummy.java

package hello;

public class Dummy{
  private String fieldA;
  private String fieldB;
  
  public Dummy(){
  }

  public String getFieldA(){
    return fieldA;
  }
  
  public String getFieldB(){
    return fieldB;
  }
  
  public void setFieldA(String arg){
    fieldA = arg;
  }
  
  public void setFieldB(String arg){
    fieldB = arg;
  }
}

ConfigHttpSession.java

/* 
http://docs.spring.io/spring-session/docs/current/reference/html5/guides/boot.html

pkg install redis #freebsd
add redis_enable="YES" to /etc/rc.conf
/usr/local/etc/rc.d/redis start

/usr/local/etc/redis.conf
requirepass 12345678

keys *
hgetall <hashkey>

Same as in application.properties spring.redis.password=12345678

https://localhost:8443/setvalue/valx
147569e4-9e6d-4363-a373-b20261513f11

https://localhost:8443/getvalue
SESSION=147569e4-9e6d-4363-a373-b20261513f11

2 instances of spring boot using the same redis server
curl -k -v --cookie "SESSION=147569e4-9e6d-4363-a373-b20261513f11" https://localhost:8443/getvalue
curl -k -v --cookie "SESSION=147569e4-9e6d-4363-a373-b20261513f11" https://localhost:8444/getvalue 
*/
package hello;

import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

@EnableRedisHttpSession
public class ConfigHttpSession{
    // springSessionRepositoryFilterbean is responsible for replacing the HttpSession with a custom implementation that is backed by Spring Session.    
}

BroadcastMessageHandler.java

package hello;

import java.nio.charset.StandardCharsets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BroadcastMessageHandler{
    private final Logger logger = LoggerFactory.getLogger(BroadcastMessageHandler.class);
    
    public void handleMessage(byte[] message){
        String msg = new String(message, StandardCharsets.UTF_8);
        logger.info("Received msg " + msg   );
    }

    public void handleMessage(String message){
        logger.info("Received msg " + message   );
    }
}

Application.java

package hello;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import java.io.FileNotFoundException;
import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Value;
import javax.inject.Inject;
import javax.annotation.Resource;
import org.springframework.util.ResourceUtils;
import org.springframework.core.env.Environment;

//-- rabbit
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.AnonymousQueue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import java.net.URI;
//-- rabbit

@ComponentScan
@EnableAutoConfiguration
@EntityScan(basePackages="hello")
public class Application {
    @Resource
    Environment env;

    private static Logger logger;

    public static void main(String[] args) {
        logger = LoggerFactory.getLogger(Application.class);
        logger.info("Starting application");
        SpringApplication.run(Application.class, args);
    }

    @Bean(name="basicDataSource",destroyMethod="close") 
    public BasicDataSource createBasicDataSource(){
      logger.info("Creating basicDataSource");
      BasicDataSource bds = new BasicDataSource();
      // get config data from application.properties
      bds.setDriverClassName(env.getProperty("db.driverclass") );
      bds.setUrl(env.getProperty("db.jdbcurl") );
      bds.setUsername( env.getProperty("db.username") );
      bds.setPassword( env.getProperty("db.password") );
      bds.setInitialSize( Integer.parseInt( env.getProperty("db.initialsizeconnpool")) );
      return bds;
    }

    @Bean(name="jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(@Autowired @Qualifier("basicDataSource") BasicDataSource bds  ){
      logger.info("Creating jdbcTemplate");
      return new JdbcTemplate( bds );
    }

//---  
    public class TCC implements TomcatConnectorCustomizer{
        Environment env;

        public TCC(Environment env){ 
            this.env=env;  
        }  

        public void customize(Connector connector){
            String absoluteKeystoreFile = "";
            logger.info(String.format("Keystore file: %s" , env.getProperty("keystore.file")   )); 

            try{ 
                absoluteKeystoreFile = ResourceUtils.getFile(env.getProperty("keystore.file")).getAbsolutePath(); 
                logger.info(String.format("Absolute path keystore: %s" , absoluteKeystoreFile )  );
            }
            catch(Exception ex){
              logger.error( ex.getMessage() );
            }

            connector.setSecure(true);
            connector.setScheme("https");
            connector.setAttribute("keystoreFile", absoluteKeystoreFile);
            connector.setAttribute("keystorePass", env.getProperty("keystore.password"));
            connector.setAttribute("keystoreType", env.getProperty("keystore.type"));
            connector.setAttribute("keyAlias", env.getProperty("keystore.alias")  );
            connector.setAttribute("clientAuth", "false");
            connector.setAttribute("sslProtocol", "TLS");
            connector.setAttribute("SSLEnabled", true);
        } 
    }

    public class ESCC implements EmbeddedServletContainerCustomizer{
        Environment env;

        public ESCC(Environment env){ 
            this.env=env;  
        }

        public void customize(ConfigurableEmbeddedServletContainer container){
            TomcatEmbeddedServletContainerFactory containerFactory = (TomcatEmbeddedServletContainerFactory) container;            
            TomcatConnectorCustomizer tcc = new TCC(env);
            containerFactory.addConnectorCustomizers(tcc);
        }
    }

    @Bean(name="containerCustomizer")
    public EmbeddedServletContainerCustomizer containerCustomizer() 
    throws FileNotFoundException {
        logger.info("Creating containerCustomizer");
        EmbeddedServletContainerCustomizer escc = new ESCC(env);
        return escc;
    }
//---

//---- rabbit

//import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
    @Bean(name="cachingConnectionFactory")
    CachingConnectionFactory createCachingConnectionFactory(){
        logger.info("Creating cachingConnectionFactory");
        String uri = env.getProperty("rabbitmq.uri");
        URI rabbitmqUri=null;
        
        try{
            rabbitmqUri = new URI(uri);
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
        
        CachingConnectionFactory ret = new CachingConnectionFactory(rabbitmqUri);
        logger.info(String.format("Created cachingConnectionFactory with hashCode %d" , ret.hashCode() ) );
        return ret;
    }
    
    @Bean(name="myQueue")
    Queue createMyQueue() {
        logger.info("Creating myQueue");
        return new Queue("myQueue", false);
    }

    @Bean(name="topicExchange")
    TopicExchange createTopicExchange() {
        logger.info("Creating topicExchange");
        return new TopicExchange("topicExchange");
    }

    @Bean(name="bindingQueue")
    Binding binding(@Qualifier("myQueue")Queue queue, @Qualifier("topicExchange")TopicExchange exchange) {
        logger.info("Creating bindingQueue");
        return BindingBuilder.bind(queue).to(exchange).with(queue.getName());
    }

    @Bean(name="workQueueContainer")
    SimpleMessageListenerContainer createWorkQueueContainer(ConnectionFactory connectionFactory, @Autowired @Qualifier("listenerAdapter") MessageListenerAdapter listenerAdapter,@Qualifier("myQueue") Queue queue) {
        logger.info("Creating createWorkQueueContainer for work queue");
        logger.info(String.format("Using ConnectionFactory with hashCode %d" , connectionFactory.hashCode() ) );
        
        SimpleMessageListenerContainer smlc = new SimpleMessageListenerContainer();
        smlc.setConnectionFactory(connectionFactory);
        smlc.setQueueNames(queue.getName() );
        smlc.setMessageListener(listenerAdapter);
        return smlc;
    }
    
    @Bean(name="listenerAdapter")    
    MessageListenerAdapter listenerAdapter(@Autowired @Qualifier("messageHandler")MessageHandler messageHandler) {
        logger.info("Creating listenerAdapter");
        return new MessageListenerAdapter(messageHandler);
    }

    @Bean(name="messageHandler")
    MessageHandler createMessageHandler() {
        logger.info("Creating messageHandler");
        return new MessageHandler();
    }
    
    //--------    
    @Bean(name="fanoutExchange")
    FanoutExchange createFanoutExchange() {
        logger.info("Creating fanoutExchange");
        return new FanoutExchange("fanoutExchange");
    }

    @Bean(name="anonymousQueue1")
    AnonymousQueue createAnonymousQueue1() {
        logger.info("Creating anonQueue1");
        return new AnonymousQueue();
    }

    @Bean(name="bindingTopic")
    Binding bindingTopic(@Qualifier("anonymousQueue1")AnonymousQueue queue, @Qualifier("fanoutExchange")FanoutExchange exchange) {
        logger.info("Creating bindingTopic");
        return BindingBuilder.bind(queue).to(exchange);
    }

    @Bean(name="broadcastMessageHandler")
    BroadcastMessageHandler createBroadcastMessageHandler() {
        logger.info("Creating broadcastMessageHandlermessageHandler");
        return new BroadcastMessageHandler();
    }
    
    @Bean(name="broadcastListenerAdapter")    
    MessageListenerAdapter createBroadcastlistenerAdapter(@Autowired @Qualifier("broadcastMessageHandler")BroadcastMessageHandler broadcastMessageHandler) {
        logger.info("Creating broadcastListenerAdapter");
        return new MessageListenerAdapter(broadcastMessageHandler);
    }

    @Bean(name="topicContainer")
    SimpleMessageListenerContainer createTopicContainer(ConnectionFactory connectionFactory, @Autowired @Qualifier("broadcastListenerAdapter") MessageListenerAdapter listenerAdapter, @Qualifier("anonymousQueue1")  AnonymousQueue anonQueue) {
        logger.info("Creating container for topic");
        logger.info(String.format("Using ConnectionFactory with hashCode %d" , connectionFactory.hashCode() ) );
        
        SimpleMessageListenerContainer smlc = new SimpleMessageListenerContainer();
        smlc.setConnectionFactory(connectionFactory);
        smlc.setQueueNames( anonQueue.getName() );
        smlc.setMessageListener(listenerAdapter);
        return smlc;
    }
    
    //----
//---- rabbit
}

persistence.xml

<persistence>
    <persistence-unit name="default" transaction-type="RESOURCE_LOCAL">
        <jta-data-source>basicDataSource</jta-data-source>
        <properties>
            <!-- <property name="hibernate.hbm2ddl.auto" value="create-drop" /> -->
            <property name="hibernate.hbm2ddl.auto" value="update" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.show_sql" value="false" />
        </properties>
    </persistence-unit>
</persistence>

greeting.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Getting Started: Serving Web Content</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <p th:text="'Hello, ' + ${name} + '!'" />
</body>
</html>

test.sql

--JDBC URL: jdbc:mariadb://localhost:3306/springmvchtml
--mysql -u root -p
create database springmvchtml;
create user 'usertest'@'%' identified by '????';
create user 'usertest'@'localhost' identified by '????';
grant all on springmvchtml.* to 'usertest'@'%';
grant all on springmvchtml.* to 'usertest'@'localhost';
show grants for 'usertest'@'%';
show grants for 'usertest'@'localhost'; 
create table springmvchtml.dummy (name varchar(255) ) ;
insert into springmvchtml.dummy (name) values('aaaa');
insert into springmvchtml.dummy (name) values('bbbb');
commit;
-- mysql -u usertest -p
  • MoinMoin Powered
  • Python Powered
  • GPL licensed
  • Valid HTML 4.01