Skip to content
This repository has been archived by the owner on Nov 25, 2017. It is now read-only.
Mattias Levin edited this page Dec 16, 2016 · 11 revisions

How to create your first sample Play sample app!!!

Install the following tools

  • Intellij (drag to you Application folder)
You need to install the Scala plugin in Intellij
1. Start Intellij
2. Select "Configure" -> "Plugins"
3. Select "Install JetBrain plugin"
4. Search for and install "Scala" plugin
5. Once installed, restart Intellij
  • Git
  • Java 8 (run java -version in terminal)
  • Postman (Chrome plugin or standalone app)
  • Playframework (activator command must be on your PATH)

Copy the downloaded activator folder to you home folder <YOUR HOME FOLDER>/activator-dist-1.X.Y/     
(in my case /Users/mattias/activator-dist-1.3.12)

1. Go to you home folder
2. List all files "ls -all"
3. type "vi .bash_profile" (you can use any other editor to create/update the file)
4. Add the following 2 lines as the end

PATH="/Users/YOUR HOME FOLDER NAME/activator-dist-1.X.Y/bin:${PATH}"
export PATH

4.5 Close vi
5. Restart your terminal 
6. type "echo $PATH" (you should see the path to activator)
(This is my path: /Users/mattias/.nvm/versions/node/v5.12.0/bin:/Users/mattias/activator-dist-1.3.12/bin:/Library/Frameworks/Python.framework/Versions/2.7/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/mattias/Scripts)

7. type "activator" in the terminal (will open Safari)

Optional

  • MySQL
  • Sequel Pro (SQL browser client) (Drag to your Application folder)

Clone Sample project

To clone and test the sample project

1. Go to your home folder (or any other folder you like to keep you projects)
2. git clone https://github.com/gnits-map-projects/PlaySampleApp.git
3. cd PlaySampleApp.git
4. ls (look around the files)
5. "activator compile" (compiles the files) NOTE! This may take a little while to complete
6. "activator test" (runs the unit tests)
7. "activator run" (starts the server)
8. open http://localhost:9000 in Safari NOTE! This may take a little while to complete

Open sample project in Intellij

**NOTE! Make sure you are using the the same folder in the terminal (to run activator commands) and in the editor! **

1. Open Intellij
2. Select import project
3. Pick the root folder of your cloned repository and Open
4. Pick "Import project from external model" and select "SBT" and next (if you do not see SBT option you need to install the Intellij Scala plugin).
5. Accept suggestions and continue
6. Make sure you select Java 1.8 as you SDK (if the dropdown is empty click the button and select the home folder of your Java installation)

Some magic stuff will happen and hopefully you will be able to import the project.

You only need to do the import once. Now you can close and open you project directly from Intellij.

Create new Project in Intellij

  • Create New Project
  • Scala -> Activator (will download available templates). If you do not see this option, please install Intellij Scala plugin.
  • Select Play Java Seed (feel free to explore other templates, great way to learn)
  • Chose location and name of project
  • Project SDK 1.8 (requires that you configured Intellij SDKs before)
  • Project will be created and dependencies will be downloaded (may take some time)

Configure Module

  • Select Root folder -> Right click -> Select Open Module Setting
  • Configure source and test folders, excluded folders (folder you do not want to see)
  • Mark .idea and select Excluded (turns red)

Hide Excluded folders

  • Select Project tab -> right click -> Uncheck Show Exclude Files

Understand Play Project structure

Visit the play homepage https://www.playframework.com/documentation/2.5.x/Anatomy

Compile, Test and Run

  • Open Termanal App
  • Type activator compile
  • Type activator test
  • Type activator run
  • Open http://localhost:9000/ in your web browser (Play compiles the code when you load it the first time)

Remove example files

Or keep if you want to look at examples

Create Hello world controller

  • Create HelloWorldController.java in controllers package
  • Extend Controller
  • Create helloWorld() method. How do we get there?
  • Add route to "config/routes" file
    GET /helloWorld controllers.HelloWorldController.helloWorld()
  • Make sure your application is running (activator run)
  • Open http://localhost:9000/helloWord in Safari or Postman

**NOTE! Make sure you import the correct classes from the correct package. Do not import classes from the .api. package, these are Scala packages

Exploring and understanding Results

  • REST API should always return the correct HTTP response code
  • 200 OK ok()
  • 201 Created created()
  • 204 No content noContent()
  • 404 Not found notFound()
  • 400 Bad request badRequest()
  • 500 Internal server error
  • Change the code and return 500 (Internal server error). Try in browser. Try in Postman

Passing parameters

  • In path
  • As query parameters
  • In Body

Path parameters

  • hello/mattias
  • Add new method helloMattias()
  • Add route
    GET /hello controllers.HelloWorldController.heloMattias()
    Notice the spelling error? Try in browser
  • Fix route
    GET /hello controllers.HelloWorldController.helloMattias()
  • Try again
  • Want to take a paramter? e.g. hello/{name}
  • Add new method "hello(String: name)"
  • Add route
    GET /hello/:name controllers.HelloWorldController.hello(name: String)

Priority matching:
GET /hello/mattiasGET /hello/:name
What would happen is we changed order?

Query Parameters

  • Support new request /hellos/mattias?count=2
  • Add method hellos(String name, count: Integer)
  • Use a string buffer to print the welcome message multiple times
  • Add route
    GET /hellos/:name controllers.HelloWorldController.hellos(name: String, count: Integer)
  • Test
  • Test and do not send a count.
  • What happens?
  • Make it optional
    GET /hellos/:name controllers.HelloWorldController.hellos(name: String, count: Integer ?= null)
  • What happened now?
  • How can we handle it inside the code? Implement
  • Provide default value
    GET /hellos/:name controllers.HelloWorldController.hellos(name: String, count: Integer ?= 2)


Pass body parameter

  • New method "helloWithFullName()" that accepts a JSON body
  • Add route
    POST /greetings controllers.HelloWorldController.helloWithFullName()
  • Test. What happened?
  • Add a Json body and try again
  • There are better ways to do bindings

Logging

  • You often what to log message to see what is happening. Both during development and especially production
  • Log levels debug, info, warn, error
  • Log hierarchy. A child logger will inherit the parent loggers level unless explicitly set
  • A root logger is always provided
  • Loggers are configures in logback.xml
  • Play provides a default logger
  • Add a log statement to hello world
    Logger.debug("logging test")
  • Test
  • Change the log level to INFO in logback.xml
  • Test
  • Change the code Logger.error("logging test")
  • Provide your custom logger for each class
  • Declare static logger in HelloWorldController.java
    private final static Logger.ALogger LOGGER = Logger.of(HelloWorldController.class);
    LOGGER.debug("My custom logger")
  • Why is not the log showing?
  • root logger level = warning, inherited by this logger
  • Change to error log
  • Test
  • Set the log level for all controllers to DEBUG in logback.xml
    <logger name="controllers" level="DEBUG" />

Library service example

Defined our model objects

  • Book
    • id
    • title
    • authors (wait with this)
  • Author
    • id
    • firstName
    • lastName

Implement models

Unit test models

  • Why do we do unit tests?
  • createAuthor test
  • createBook test
  • Run activator test in Terminal to run init tests
  • Show what happen if a test fails

I want you to try to implement unit test in your project

Other types of test

  • Unit tests
  • Integration test
  • Application / UI tests

Views

  • Will return JSON
  • No need to implement any web pages (yet)
  • What Views do we need
    • Get all books
    • Get book by id
    • Create book
    • Update book by id
    • Delete book by id

Controllers

  • BooksController.java
    • getAllBook()
    • getBookById(Long id)
    • createBook()
    • updateBookWithId(Long id)
    • deleteBookWithId(Long id)
  • Check parameters, return bad request
  • Implement and return mock data (use List of books instance variable)

Body Parsers

  • We have code duplication
  • Update createBook() and updateBookWithId(Long id) to use body parser
  • Body parsers give you the change to parse the body into an object before your action is called
  • Implement BookBodyParser.java class, extends BodyParser<Book>

Integration test

TBD

Databases

  • What is missing?
  • What happens when we restarts?
  • We need a DB and a persistence framework to talk to the DB.
  • Add H2 (H2 offers both in memory DB and disk). Good for development and testing, should not use in production.
  • Update the following lines for code in "application.config" file
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"	
db.default.jndiName=DefaultDS
  • Restart Play app
  • Make a request and look at the logs
  • Add JPA dependencies to build.sbt file (this will download the necessary JPA dependencies)
    libraryDependencies ++= Seq(
 javaJpa,
 "org.hibernate" % "hibernate-entitymanager" % "5.1.0.Final")
  • Configure JPA persistence store
  • Add a persistence.xml in conf/META-INF
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">

    <persistence-unit name="defaultPersistenceUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <non-jta-data-source>DefaultDS</non-jta-data-source>
        <properties>
            <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
        </properties>
    </persistence-unit>

</persistence>
  • The "drop-and-create" instruction will tell JPA to delete all tables and create them each time the server is started. You can use only "create" or skip completely. It is however useful when creating the DB schema the first time.
  • add jpa.default=defaultPersistenceUnit to config file

JPA

  • One of the most common DB frameworks in Java
  • JPA uses Entities and EntityManager

Entities

  • Maps your models into DB tables and columns
  • Add @Entity to Book class (class can not be final?)
  • Annotate properties that should be persisted
    • @Id
    • @GeneratedValue(strategy = GenerationType.AUTO)
    • @Basic

EntityManager

  • Inject EntityManager in BookController.java and store as instance variable.
...
private JPAApi jpaApi;

@Inject
public BookController(JPAApi jpaApi) {
   this.jpaApi = jpaApi;
}
...
  • What is Dependency injection?
  • Query for all books in getAllBooks()
TypedQuery<Book> query = jpaApi.em().createQuery("SELECT b FROM Book b");
List<Book> books = query.getResultList();
  • Must annotate with @Transactional
  • Need to generate DB schema (table, columns etc)
  • Auto create schema from annotations (we can also create the schema manually using SQL)
    <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
  • Update createBook() method to use the DB - jspApi.persist(book);
  • Must annotate with @Transactional
  • Test
  • Migrate remaining methods
    • getBookById(Integer id)
    • updateBookById(Integer id)
    • deleteBookById(Integer id)

Unit tests with Entities

TBD

Data Abstraction Layer

The code above has problems

  • Fat controllers
  • Persistence framework exposed to controllers - hard to change to other framework in the future
  • Eventually it will become spaghetti code
  • We need some kind of abstraction/layer

Common frameworks

  • Repository
  • Data Access Objects (Dao)
  • ActiveRecord

Our first Dao

  • Create dao package
  • Implement BookDao.java
    • persist(Book book)
    • deleteBook(Long id)
    • findById(Long id)
    • update(Book book)
    • findAll()

Implement Dao Interface

  • Since we do not know the type we need to use generics
  • Let BookDao class implement the Dao interface

Provide default interface implementation of the Dao interface

  • Remove the code from BookDao class
  • Implement AuthorDao class with just a few lines of code

Change the H2 database to store on file

  • default.url = "jdbc:h2:~/h2_test"
  • <property name="javax.persistence.schema-generation.database.action" value="create"/>

How how we connect to a MySQL server

  1. Install MySQL on computer
  2. Start
  3. Create data and schema (user and password)
  4. Change or DB config in our Play app
  5. Add db driver dependency
  6. Connect from application