# Spring Boot Features

Get familiar with the main Spring Boot features.

Time: 25 minutes.

## Directory Web Features

This lab will show you how to use some of the Spring Boot features. You are going to use the code from previous labs (directory-web-internals project)

1. Open a browser and hit the url: http://start.spring.io

2. Click the Switch to the full version link.

3. Fill out the Directory Web App Project metadata with (See Figure 1):

   Table 1. Directory Web App - metadata
   Property | Value
   --- | ---
   Group: | io.pivotal.workshop
   Artifact: | directory-web-features
   Name: | directory-web-features
   Package Name: | io.pivotal.workshop.directory
   Dependencies: | Web, AOP
   Spring Boot: | 2.0.0.M7

   Figure 1: Spring Initializr - http://start.spring.io

   Tip | You can choose either Maven or Gradle project types.

4. Type Web and AOP in the Dependencies field and press Enter.

5. Click the Generate Project button.

6. Unzip the file in any directory you want.

7. Import your project in any IDE you want.

8. You can copy all the code from the previous lab (we are going to use it).

### SpringBootApplication: Banner and Web Environment

Let’s start with the SpringBootApplication class. In the main source code: DirectoryWebFeaturesApplication class, add the following code:

io.pivotal.workshop.directory.DirectoryWebFeaturesApplication.java

    package io.pivotal.workshop.directory;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;

    @SpringBootApplication
    public class DirectoryWebFeaturesApplication {

            public static void main(String[] args) {

                    SpringApplication app = new SpringApplication(DirectoryWebFeaturesApplication.class);
                    app.run(args);
            }
    }

the SpringApplication will be use to turn on/off some features.

#### Challenges

- Add a banner.txt with some ASCII ART in the src/main/resources folder and run the application. You can choose some ascii art from: http://patorjk.com/software/taag

- Turn off the Banner using the app instance.

- Turn off the Web Environment using the app instance.

- You can turn on/off the Banner and Web Environment using the application.properties, find out how.

### SpringBootApplication: CommandLineRunner / ApplicationRunner

In the main source code: DirectoryWebFeaturesApplication class, replace the class definition with the following code:

io.pivotal.workshop.directory.DirectoryWebFeaturesApplication.java

    package io.pivotal.workshop.directory;

    import java.util.stream.Stream;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;

    @SpringBootApplication
    public class DirectoryWebFeaturesApplication {

            public static void main(String[] args) {

                    SpringApplication app = new SpringApplication(DirectoryWebFeaturesApplication.class);
                    app.run(args);
            }

        private static final Logger log = LoggerFactory.getLogger("[ARGUMENTS]");

        @Bean
            public CommandLineRunner commandRunner() {
                    return args -> {
                            Stream.of(args).forEach(s -> {
                                    log.info("CommandLine: " + s);
                            });
                    };
            }
    }

the CommandLineRunner is use to execute code before your Spring Boot application start. The previous code will log all the arguments passed to the application.

Package and run the application with the following arguments: --option=A,B,C --enable-audit=yes

+ .create the JAR

    ./mvnw clean package -DskipTests=true

+ .run the application with some arguments

    java -jar target/directory-web-features-0.0.1-SNAPSHOT.jar --option=A,B,C --enable-audit=yes

see the logs.

#### Challenges

- Use now the ApplicationRunner and get the option and enable-audit values, you should log something similar:

      [ARGS]  : Option Name: enable-audit
      [ARGS]  : Option Name: option
      [ARGS]  : Found Value:[A,B,C]
      [ARGS]  : Found Value:[yes]
      [ARGS]  : Argument: --option=A,B,C
      [ARGS]  : Argument: --enable-audit=yes

### SpringBootApplication: External Configuration

If you didn’t finish the previous lab (directory-web-internals) this is you chance! Create a DirectoryProperties class that will hold the information about audit (on/off) and the info to be display (long/short) for the AOP audit.

Here you will use the @ConfigurationProperties annotation to enable a properties binding, you will use the prefix directory.

io.pivotal.workshop.directory.config.DirectoryProperties.java

    package io.pivotal.workshop.directory.config;

    import org.springframework.boot.context.properties.ConfigurationProperties;

    @ConfigurationProperties(prefix = "directory")
    public class DirectoryProperties {

            private String audit = "off";
            private String info = "long";

            public String getAudit() {
                    return audit;
            }

            public void setAudit(String audit) {
                    this.audit = audit;
            }

            public String getInfo() {
                    return info;
            }

            public void setInfo(String info) {
                    this.info = info;
            }

    }

Modify the application.properties by adding the new properties:

src/main/resource/application.properties

    directory.audit=on
    directory.info=short

Create the aspect DirectoryAudit and read the values to audit the method findByEmail from the DirectoryRepository class. (This is the challenge from the directory-web-internals lab).

io.pivotal.workshop.directory.aop.DirectoryAudit.java

    package io.pivotal.workshop.directory.aop;

    import java.util.stream.IntStream;

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    import io.pivotal.workshop.directory.annotation.Audit;
    import io.pivotal.workshop.directory.config.DirectoryProperties;

    @Aspect
    public class DirectoryAudit {

            private DirectoryProperties props;

            public DirectoryAudit(DirectoryProperties props){
                    this.props = props;
            }

            private static Logger log = LoggerFactory.getLogger("[AUDIT]");

            @Around("execution(* *(..)) && @annotation(audit)")
            public Object audit(ProceedingJoinPoint jp, Audit audit) throws Throwable {
                    Object[] args = jp.getArgs();

                    this.printBar();
                    this.print("[executing] " + (props.getInfo().toLowerCase().equals("short") ? jp.getSignature().getName() : jp.getSignature() ));

                    switch (audit.value()) {
                    case BEFORE:
                    case BEFORE_AND_AFTER:
                            this.printArgs(args);
                    default:
                            break;
                    }

                    Object obj = jp.proceed(args);

                    switch (audit.value()) {
                    case AFTER:
                    case BEFORE_AND_AFTER:
                            this.print("[result] " + obj);
                    default:
                            this.printBar();
                            break;
                    }

                    return obj;
            }

            private void print(Object obj) {
                    log.info(obj.toString());
            }

            private void printArgs(Object[] args) {
                    IntStream.range(0, args.length).forEach(idx -> {
                            log.info(String.format("[params] arg%d = %s", idx, args[idx]));
                    });

            }

            private void printBar(){
                    log.info("===========");
            }
    }

In the DirectoryRepository add the @Audit annotation in the findByEmail method.

io.pivotal.workshop.directory.repository.DirectoryRepository.java - snippet

    @Audit
    public Optional<Person> findByEmail(String email){
            return findFirstBy( p -> p.getEmail().equals(email));
    }

Run you application and check out the logs.

Warning | The on/off feature only works if you did the challenge from the previous lab. You need to add the @ConditionalOnProperty to evaluate if the directory.audit is on or off.

#### Challenges

- Run you application by testing the @Audit annotation values: Auditor.BEFORE,Auditor.AFTER,Auditor.BEFORE_AND_AFTER

- Package your application and:

  - override the audit and info properties in the command line.

  - use environment variables to override the audit and info properties.

  - create a application.yml file in the current directory, add the audit and info properties and execute the program, what happen? did it worked?

Tip | Remember that, by adding an application.properties or application.yml to the current directory will override the values.

### [HOMEWORK]

- Create profiles by:

  - Creating an application-qa.properties and application-prod.properties. Add the properties audit and info and test by enable the two profiles.

  - Creating an application.yml and add the profile section. Test by enable each profile.

- Instead of using the @Configuration properties, use application arguments to enable the audit and info.