Let’s upgrade our minimal SBT project into a minimal PlayFramework project. Here is a description from official documentation: https://www.playframework.com/documentation/2.6.x/BuildOverview
Let’s check the latest version of PlayFramework here: https://mvnrepository.com/artifact/com.typesafe.play/play
We will first add a Play plugin to project/plugins.sbt:
This has no effect on our project yet. SBT compile still behaves the same. We need to tell, that our project has to use the Play plugin for compilation and running. We add following to
PlayScala is a new object, now available in classpath. You can navigate to it’s definition from IntelliJ. This plugin takes over compilation of source files in our project. Sources are now located in different directories. Let’s try to execute “sbt run”. It now behaves differently – it runs a web server on localhost:9000. Let’s open a browser and navigate to http://localhost:9000
Configuration error application: application.conf: java.io.IOException: resource not found on classpath: application.conf, application.json: java.io.IOException: resource not found on classpath: application.json, application.properties: java.io.IOException: resource not found on classpath: application.properties
Let’s add an empty file “conf/application.conf” and “sbt run” again.
Unexpected exception RuntimeException: No application loader is configured. Please configure an application loader either using the play.application.loader configuration property, or by depending on a module that configures one. You can add the Guice support module by adding "libraryDependencies += guice" to your build.sbt.
The plugin is now missing Application Loader. Think of it as the very first entry point of your Play application.
Raw application loader
At this point, you could try to implement your custom Application Loader by implementing the
Applicationinterfaces. Just add one new file:
And point SBT plugin to this application loader. Edit
Of course, after page refresh, you will immediatelly get NotImplementedError. The interface is not an easy one to implement by hand. It represents a substantial part of what PlayFramework provides out of box. While this is not really an useful example, it demonstrates, to which extends you are able to customize most basic components of the framework itself.
Custom application loader
You can find most of missing pieces in the class
play.api.BuiltInComponentsFromContext. The only missing component is the router. In following example, we will define a router by hand. As you can see, a router is simply a partial function from
RequestHeader to a class, which is tagged with the interface
play.api.mvc.Handler. In our example, we make use of
SimpleRouter and as the handler, we will provide a
play.api.mvc.Action. The action demonstrates how to read headers, body, configuration and send back an output.
Our custom application loader returns the same response for every URI, that we throw at it. That is clearly the result of having a partial function which matches all requests using Scala’s wildcard operator
_. In real world, you want to bind different actions to different URIs. Modern web framework always provide some kind of routing support, by defining URI patterns and binding actions to them. You could implement routing by hand too, by implementing multiple cases of the partial function.
Play framework plugin comes with a handy feature: routes file. This file is parsed and compiled and as a result a generated Scala source file appears. Let’s create such file. As you can see, all GET and POST requests to URI starting with
/foo/ will be dispatched to method
foo of instance of class
Also, during compilation, one file will be created:
router.Routes. Now, if you are inside an IDE and you modify
routes, you need to compile your project. Don’t worry, if your project is not compilable. Routes are compiled and generated first.
Here is your first controller. That’s how these classes are usually called in web frameworks that follow MVC pattern. You should note, that the constructor takes three parameters, which from now on we shall call dependencies.
Finally, we also have to modify our application loader:
At this point, you might have discovered SBT shell and executing tasks from the shell. You must not forget though, that changes in build.sbt and project/plugins.sbt and project/build.properties are not refreshed automatically. You need to execute the “reload” command in the shell.
In next part, we will take a look the Application Loader again and introduce dependency injection.