This Play 2.4 template tries to be a skeleton for a simple multidomain project (www.myweb.com and admin.myweb.com). It shows you how to use subprojects for that and how to share common code. It is also ready to use with Webjars, CoffeeScript, LESS, RequireJS, assets Gzip and assets fingerprinting. Please, check the readme file for more details.
The bundle includes a small bootstrap script that
can start Activator. To start Typesafe Activator's UI:
In your File Explorer, navigate into the directory that the template was extracted to, right-click on the file named "activator.bat", then select "Open", and if prompted with a warning, click to continue:
We've included the text of this template's tutorial below,
but it may work better if you view it inside Activator
on your computer. Activator tutorials are often designed
to be interactive.
Preview the tutorial
Let's suppose you want to develop a whole project which has two different services: the typical public webpage and the private admin one. You also want a specific subdomain for the admin webpage, so we will have:
admin.myweb.com: private administration webpage.
www.myweb.com or myweb.com: public webpage.
And let's also suppose you prefer to have these services isolated in your production server. So you would be able to manage them separately (with different PIDs, different ports, different resources…).
Then, we have the following objectives:
Development should be simple. activator run should be enough to run all services at the same time.
Common code, dependencies and modules should be easily shared.
We should be able to compile, test and run each service separately in development and production.
We should distribute each service separately.
It should be a template ready to use with the following features:
How to share every common code to avoid duplications (models, controllers, views, CoffeeScript, LESS, ...).
How to use it for development, test and production.
This template has 3 subprojects:
web: will contain all the specific code for the public webpage service.
admin: will contain all the specific code for the private administration webpage service.
common: will contain all the common code shared between the other subprojects.
Obviously, this is a template, so you can easily change its names or add more modules. Once you understand how it works you will find it easy to modify.
You can see the basic structure of the whole project
Let's try to explain briefly how it is configured. For running the whole project we have the following configuration files:
build.sbt: configures root project and declares every subproject.
conf/root-dev.conf(used when whole project is running): the default one. In the next section it is explained in detail.
conf/routes(used when whole project is running): routes file for the whole project. It simply imports the routes file of every subproject.
app/RequestHandler.scala(used when whole project is running): the RequestHandler object for the whole project. It determines the subdomain for each request (admin or web) and delegates its behaviour to the corresponding subproject.
As you can see, we have some shared configuration files: shared.dev.conf and shared.prod.conf. And we need them for every project (the root one and the subprojects). Both files shoud be replicated within each conf directory for every subproject. But there is an easy way to avoid code replication and minimize errors, and it's defining a new resourceGenerator within Common.scala. Then, each time the code is compiled, every shared.*.conf file will be replicated within the corresponding path. Note these files will only be generated within the target file.
It has been added a key called this.file in many of the configuration files and it is shown in the index web page when you run it. Please, play with it to see how it is overridden by each configuration file depending the project and mode (dev or prod) you are running.
The corresponding configuration file is correctly taken for each case thanks to the settings lines in Common.scala:
And add the following in the routes files (check it here):
GET /public/*file controllers.web.Assets.public(path="/public", file: Asset)
GET /lib/*file controllers.web.Assets.lib(path="/public/lib", file: Asset)
GET /css/*file controllers.web.Assets.css(path="/public/stylesheets", file: Asset)
GET /img/*file controllers.web.Assets.img(path="/public/images", file: Asset)
GET /common/css/*file controllers.web.Assets.commonCss(path="/public/lib/common/stylesheets", file: Asset)
GET /common/img/*file controllers.web.Assets.commonImg(path="/public/lib/common/images", file: Asset)
You can put the common public files in the subproject common, within the folder modules/common/public/. The common Assets are packaged as Webjars for the other subprojects that depend on it, so you must access to them through their correspoding lib folder:
If you have shared resources between your subprojects, like for example uploaded images from your users, you need to render or download them from a shared folder. Note you can't consider a shared resource like a asset.
When running the whole project, the global one will determine the subdomain for each request and delegate its behaviour to the corresponding subproject. Remember that is necessary to declare each specific ErrorHandler withing the corresponding configuration file.
If you have doubts about the specific route of any webjar resource, remember it is directly downloaded within the relative folder target/web/web-modules/main/webjars/lib. So you can easily check the file structure that has been downloaded by the webjar.
For more information, go to the documentation page about CoffeeScript.
The corresponding plugin needs to be active in file project/plugins.sbt. And the next configuration has been added to every subproject to be able to work with partial LESS source files (in project/Common.scala):
includeFilter in (Assets, LessKeys.less) := "*.less"
excludeFilter in (Assets, LessKeys.less) := "_*.less"
With that, every LESS file not prepended by an underscore (_) will be compiled, and they could import the code from the LESS files prepended by an underscore.
Each time the code is compiled, every needed messages file will be generated appending the corresponding previous ones. Note these files will only be generated within the target file.
Assumption: if there are 2 coincidences within the same file, the last one will be taken. So it is ordered from lower to higher priority.
First of all, to get access to admin subdomain you will need modify your /etc/hosts files (or the equivalent in your S.O.) to map the next URLs to localhost or (127.0.0.1). For example, add the following lines: