# Spring Boot Web

Get familiar with the Spring MVC and the Spring Boot Web applications.

Time: 30 minutes.

## Code Snippet Manager

As developers normally we require to have some code snippets that help us to code fast and safe.

The main idea of this application is to create a site that can manage our Code Snippets. We are going to create a RESTful API for any external client.

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 Code Snippet Manager Project metadata with (See Figure 1.0):

   Table 1. Code Snippet Manager App - metadata
   Property | Value
   --- | ---
   Group: | io.pivotal.workshop
   Artifact: | code-snippet-manager
   Name: | code-snippet-manager
   Package Name: | io.pivotal.workshop.snippet
   Dependencies: | Web, DevTools, Groovy Templates
   Spring Boot: | 2.0.0.M7

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

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

4. Type Web, DevTools, Groovy Templates, 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. Let’s start by defining the domain models. Create the following classes:

   io.pivotal.workshop.snippet.domain.Language.java

       package io.pivotal.workshop.snippet.domain;

       public class Language {

               private String id;
               private String name;
               private String syntax = "text";

               public Language() {
                       this.id = java.util.UUID.randomUUID().toString().replaceAll("-", "");;
               }

               public Language(String name) {
                       this();
                       this.name = name;
               }

               public Language(String name, String syntax) {
                       this(name);
                       this.syntax = syntax;
               }

               public String getId() {
                       return id;
               }

               public void setId(String id) {
                       this.id = id;
               }

               public String getName() {
                       return name;
               }

               public void setName(String name) {
                       this.name = name;
               }

               public String getSyntax() {
                       return syntax;
               }

               public void setSyntax(String syntax) {
                       this.syntax = syntax;
               }
       }

   We can have multiple snippets in different programming languages, that’s why we have this class. Take a look that also there is a syntax field; this field will be use later on for syntax highlight.

   io.pivotal.workshop.snippet.domain.Code.java

       package io.pivotal.workshop.snippet.domain;

       public class Code {
               private String id;
               private String source;

               public Code() {
                       this.id = java.util.UUID.randomUUID().toString().replaceAll("-", "");;
               }

               public Code(String source) {
                       this();
                       this.source = source;
               }

               public String getId() {
                       return id;
               }

               public void setId(String id) {
                       this.id = id;
               }

               public String getSource() {
                       return source;
               }

               public void setSource(String source) {
                       this.source = source;
               }

       }

   This class will be use to hold the actual snippet code.

   io.pivotal.workshop.snippet.domain.Snippet.java

       package io.pivotal.workshop.snippet.domain;

       import java.util.Date;

       public class Snippet {

               private String id;
               private String title;
               private String keywords;
               private String description;
               private Language lang;
               private Code code;
               private Date created;
               private Date modified;

               public Snippet() {
                       this.id = java.util.UUID.randomUUID().toString().replaceAll("-", "");
                       this.created = new Date();
                       this.modified = new Date();
               }

               public Snippet(String title, String keywords, String description, Language lang, Code code) {
                       this();
                       this.title = title;
                       this.keywords = keywords;
                       this.description = description;
                       this.lang = lang;
                       this.code = code;
               }

               public Snippet(String title, Language lang, Code code) {
                       this(title, "", "", lang, code);
               }

               public String getId() {
                       return id;
               }

               public void setId(String id) {
                       this.id = id;
               }

               public String getTitle() {
                       return title;
               }

               public void setTitle(String title) {
                       this.title = title;
               }

               public Language getLang() {
                       return lang;
               }

               public void setLang(Language lang) {
                       this.lang = lang;
               }

               public String getDescription() {
                       return description;
               }

               public void setDescription(String description) {
                       this.description = description;
               }

               public String getKeywords() {
                       return keywords;
               }

               public void setKeywords(String keywords) {
                       this.keywords = keywords;
               }

               public Code getCode() {
                       return code;
               }

               public void setCode(Code code) {
                       this.code = code;
               }

               public Date getCreated() {
                       return created;
               }

               public void setCreated(Date created) {
                       this.created = created;
               }

               public Date getModified() {
                       return modified;
               }

               public void setModified(Date modified) {
                       this.modified = modified;
               }
       }

   The above class will be the main reponse.

9. Next let’s create a base interface that will be use as main repository. In this case we are going to hold all the data in Memory using collections:

   io.pivotal.workshop.snippet.repository.SimpleRepository.java

       package io.pivotal.workshop.snippet.repository;

       import java.util.Collection;

       public interface SimpleRepository<T> {

               Iterable<T> findAll();
               void saveAll(Collection<T> items);
               T saveAll(T item);
               T findById(String id);
       }

   as you can see it has just the most common actions.

10. Let’s create now all the Repositories that will be implementing the SimpleRepository interface.

    io.pivotal.workshop.snippet.repository.LanguageRepository.java

        package io.pivotal.workshop.snippet.repository;

        import java.util.ArrayList;
        import java.util.Collection;
        import java.util.List;
        import java.util.Optional;
        import java.util.stream.StreamSupport;

        import org.springframework.stereotype.Repository;

        import io.pivotal.workshop.snippet.domain.Language;

        @Repository
        public class LanguageRepository implements SimpleRepository<Language>{

            private List<Language> languages = new ArrayList<>();

                public Iterable<Language> findAll(){
                        return languages;
                }

                public void saveAll(Collection<Language> languages){
                        this.languages.addAll(languages);
                }

                public Language findById(String name) {
                        Optional<Language> language = StreamSupport
                                .stream(this.languages.spliterator(), false)
                                .filter(lang -> lang.getName().equals(name))
                                .findFirst();

                        if (language.isPresent()) return language.get();

                        return null;
                }

                public Language saveAll(Language item) {
                        assert item.getName() != null;

                        Language language = this.findById(item.getName());

                        if(language == null) {
                                this.languages.add(item);
                                return item;
                        }else {
                                language.setName(item.getName());
                                language.setSyntax(item.getSyntax());
                                return language;
                        }
                }
        }

    io.pivotal.workshop.snippet.repository.CodeRepository.java

        package io.pivotal.workshop.snippet.repository;

        import java.util.ArrayList;
        import java.util.Collection;
        import java.util.List;
        import java.util.Optional;

        import org.springframework.stereotype.Repository;

        import io.pivotal.workshop.snippet.domain.Code;

        @Repository
        public class CodeRepository implements SimpleRepository<Code>{

                private List<Code> codes = new ArrayList<>();

                public List<Code> findAll() {
                        return codes;
                }

                @Override
                public void saveAll(Collection<Code> items) {
                        this.codes.addAll(items);
                }

                @Override
                public Code saveAll(Code item) {
                        assert item.getSource() != null;

                        Code code = findById(item.getId());

                        if(code == null){
                                this.codes.add(item);
                                return item;
                        }
                        else {
                                code.setSource(item.getSource());
                                return code;
                        }
                }

                @Override
                public Code findById(String id) {
                        Optional<Code> code = codes
                                        .stream()
                                        .filter(c -> c.getId().equals(id))
                                        .findFirst();
                        if (code.isPresent()) return code.get();

                        return null;
                }

        }

    io.pivotal.workshop.snippet.repository.SnippetRepository.java

        package io.pivotal.workshop.snippet.repository;

        import java.util.ArrayList;
        import java.util.Collection;
        import java.util.Date;
        import java.util.List;
        import java.util.Optional;

        import org.springframework.stereotype.Repository;

        import io.pivotal.workshop.snippet.domain.Snippet;

        @Repository
        public class SnippetRepository implements SimpleRepository<Snippet>{

                private List<Snippet> snippets = new ArrayList<>();

                @Override
                public Snippet saveAll(Snippet snippet){
                        assert snippet.getTitle() != null;
                        assert snippet.getCode() != null;
                        assert snippet.getLang() != null;

                        Snippet _snippet =  null;

                        if (snippet.getId() == null) {
                                _snippet = new Snippet(snippet.getTitle(), snippet.getLang(),snippet.getCode());

                        } else {
                                 _snippet = this.findById(snippet.getId());
                                 if(_snippet != null){
                                         _snippet.setTitle(snippet.getTitle());
                                         _snippet.setCode(snippet.getCode());
                                         _snippet.setLang(snippet.getLang());
                                         _snippet.setModified(new Date());
                                 }
                        }

                        return _snippet;
                }

                @Override
                public Iterable<Snippet> findAll(){
                        return this.snippets;
                }

                @Override
                public Snippet findById(String id){
                        Optional<Snippet> result = snippets.stream()
                                        .filter(snippet -> snippet.getId().equals(id))
                                        .findFirst();

                        if (result.isPresent()) return result.get();

                        return null;
                }

                @Override
                public void saveAll(Collection<Snippet> items) {
                        this.snippets.addAll(items);
                }
        }

11. Next lets do the main controller that will expose the REST API:

    io.pivotal.workshop.snippet.controller.SnippetController.java

        @RestController
        public class SnippetController {

                private SnippetRepository snippetRepository;
                private LanguageRepository languageRepository;

                public SnippetController(SnippetRepository snippetRepository,LanguageRepository languageRepository){
                        this.snippetRepository = snippetRepository;
                        this.languageRepository = languageRepository;
                }

                @RequestMapping("/")
                public ModelAndView home(){
                        assert snippetRepository != null;

                        Map<String,Object> model = new HashMap<String,Object>();
                        model.put("langs", languageRepository.findAll());
                        model.put("snippets", snippetRepository.findAll());

                        return new ModelAndView("views/home",model);
                }

                @RequestMapping("/snippets")
                public ResponseEntity<?> snippets(){
                        assert snippetRepository != null;
                        return ResponseEntity.ok(snippetRepository.findAll());
                }
        }

12. Create a configuration class to initialize your repositories:

    io.pivotal.workshop.snippet.config.SnippetConfiguration.java

        package io.pivotal.workshop.snippet.config;

        import java.nio.file.Files;
        import java.nio.file.Paths;
        import java.util.ArrayList;
        import java.util.List;

        import org.springframework.boot.CommandLineRunner;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;

        import io.pivotal.workshop.snippet.domain.Code;
        import io.pivotal.workshop.snippet.domain.Language;
        import io.pivotal.workshop.snippet.domain.Snippet;
        import io.pivotal.workshop.snippet.repository.SnippetRepository;

        @Configuration
        public class SnippetConfiguration {

                @Bean
                public CommandLineRunner runner(SnippetRepository snippetRepo) {
                        return args -> {
                                @SuppressWarnings("serial")
                                List<Snippet> snippets = new ArrayList<Snippet>() {
                                        {
                                                add(new Snippet("Hello World", new Language("HTML", "xml"),new Code(new String(Files.readAllBytes(Paths.get("code/html-code.txt"))))));
                                                add(new Snippet("Hello World", new Language("C#", "c#"),new Code(new String(Files.readAllBytes(Paths.get("code/cs-code.txt"))))));
                                                add(new Snippet("Hello World", new Language("Pascal", "py"),new Code(new String(Files.readAllBytes(Paths.get("code/pas-code.txt"))))));
                                                add(new Snippet("Hello World", new Language("Erlang", "erl"),new Code(new String(Files.readAllBytes(Paths.get("code/erl-code.txt"))))));
                                                add(new Snippet("Hello World", new Language("JavaScript", "js"),new Code(new String(Files.readAllBytes(Paths.get("code/js-code.txt"))))));
                                                add(new Snippet("Hello World", new Language("Groovy", "groovy"),new Code("println 'Hello World'")));
                                        }
                                };

                                snippetRepo.saveAll(snippets);

                        };
                }
        }

    as you can see from the above code, we are building up our data.

    If you take a close look, we are using the Files.readAllBytes helper class to read from a file, and this file is located in the code/ folder.

13. Add some code snippets in the code/ folder.

    code/html-code.txt - HTML

        <html>
          <body>
            <h1>Hello World</h1>
          </body>
        </html>

    code/cs-code.txt - C#

        class Hello {
         static void Main() {
          System.Console.Write("Hello World");
         }
        }

    code/pas-code.txt - Pascal

        program HelloWorld;

        begin
          writeln('Hello World');
        end.

    code/erl-code.txt - Erlang

         -module(hello).
         -export([hello_world/0]).

         hello_world() -> io:fwrite("hello, world\n").

    code/js-code.txt - JavaScript

        console.log("Hello World");

14. A lot of code, right? Well now its time to run the application. You can use your IDE or command line. Once running you application, go to your browser and hit: http://localhost:8080/snippets and you should get the a response like Figure 2.0.

    Figure 2.0: Snippet Code Manager - http://localhost:8080/snippets

## Challenges

### Adding a UI - Home Page

You can see that the io.pivotal.workshop.snippet.controller.SnippetController.java has the home() method, and it returns the view: views/home and the model, a map that contains the languages and the snippets: Add the following missing dependencies to your pom.xml because you will need them:

    <dependency>
        <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>2.2.4</version>
    </dependency>
    <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>3.3.6</version>
    </dependency>
    <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>angularjs</artifactId>
            <version>1.5.7</version>
    </dependency>

Add the following libraries in the src/main/resources/static/css: theme.css and offcanvas.css and in the src/main/resources/static/js folder add the syntaxhighlighter.js script.

- Create the necessary layout and home page to have the same as Figure 3.0.

  Figure 3.0: Code Snippet Manager - http://localhost:8080

Tip | You can take a look at the Groovy Template Engine: http://docs.groovy-lang.org/next/html/documentation/template-engines.html#_the_markuptemplateengine

### REST API

- Complete the RESTful API:

  - provide the /snippets/{id} path endpoint to search by snippet Id.

  - provide methods to handle the: POST, PUT for adding a new snippet and updating the snippet.

- Make changes to repose either JSON or XML.

- When doing a XML response, the snippet code should be in a CDATA tag. Modify the code to show a CDATA for every source code. (tip: @JacksonXmlCData)

## [HOMEWORK]

- Add validation to the POST and PUT methods (tip: @Valid).

- Finish up any missing UI or Controller methods.