-
Notifications
You must be signed in to change notification settings - Fork 1
Home
How to create your first sample Play sample app!!!
- 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)
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
-
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)
- 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 selectExcluded
(turns red)
- Select
Project
tab -> right click -> UncheckShow Exclude Files
Visit the play homepage https://www.playframework.com/documentation/2.5.x/Anatomy 
- 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)
Or keep if you want to look at examples
- 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
- 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
- In path
- As query parameters
- In Body
- 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/mattias
GET /hello/:name
What would happen is we changed order?
- 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)
- 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
- 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" />
- Book
- id
- title
- authors (wait with this)
- Author
- id
- firstName
- lastName
Implement 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
- 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
-
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)
- We have code duplication
- Update
createBook()
andupdateBookWithId(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, extendsBodyParser<Book>
TBD
- 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
- One of the most common DB frameworks in Java
- JPA uses Entities and EntityManager
- 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
- Inject
EntityManager
inBookController.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)
TBD
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 theDao
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
- Install MySQL on computer
- Start
- Create data and schema (user and password)
- Change or DB config in our Play app
- Add db driver dependency
- Connect from application