Efficient Git Submodules for Play Application with Sub-Projects: A Step-by-Step Guide

adminUncategorized

Atlassian Cloud

Atlassian announced the hosted cloud solution of their products on Atlassian Camp in 2014. And, of course, the Exalate team stood before the goal to add cloud support to our product. We already have the play rest application for synchronization with Hewlett-Packard Quality Center. In this solution, we have two separate applications. First one is for configuration and another one is for all synchronization stuff.

The main problem we faced was the configuration. We wanted to make the configuration of our add-on the same way as it was in our server solution.

And this article is about the advantages and disadvantages of embedding one play application into another.

 

First step
“Prepare the play application to become a child”

 

Trailor

Play subprojects documentation says that “Configuration and route file names must be unique in the whole project structure. Particularly, there must be only one application.conf file and only one routes file.” So, foremost, we need to rename these files and check the duplicating routes and configs in the parent project.

application.conf -> child.conf

main.routes -> child.routes

How play application will find the config file and the routes?

It won’t =(. To resolve this, we will need to add the additional configuration to our child.conf and to the JVM Option of our launcher we need to add to child.conf:

play.http.router=child.Routes

 

to JVM Option:

sbt -Dconfig.resource=child.conf run

 

If your child application has static resources (js, CSS etc) we are going to have a problem with getting these resources. All these resources will be lost because of changing the location. In default play application we can easily get all the resources by defining the assets’ path in the main.routes file:

GET /assets/*file    controllers.Assets.at(path="/public", file)

 

But when our project becomes a subproject, all resources will be generated in target/web/public/main/lib/ {submodule.name} instead of target/web/public. To resolve this problem, we will create our own implementation of AssetsBuilder:

object ChildAssets extends controllers.AssetsBuilder(LazyHttpErrorHandler) {
  lazy val additionalPath = if(play.Play.application().configuration().getBoolean("child.submodule")) "/lib/child/" else ""
  override def at(path: String, file: String, aggressiveCaching: Boolean): Action[AnyContent] = {
    super.at(path + additionalPath, file, aggressiveCaching)
  }
}

As you can see, we override the at function, and on the base of the configuration property, we will look for resources in different places. And the last thing we need is to add some configuration in our build.sbt file to use a specific set of settings in development mode for a sub project:

.devSettings += ("play.http.router", "hub.Routes")

Second step Teleportation”

Teleportation

When we prepare the child project and you want just to split the big application into several parts you can skip this step and go to the step three. But what if you want to embed the child application, while still being able to run it as a separate one and store it in a separate repository? For this purpose, we can use the git sub-modules.

From GIT documentation: “A sub-module allows you to keep another Git repository in a sub-directory of your repository.”

On the internet we can find a lot of blogs and articles that will say as: “Why sub-module is evil”, “1001 stories about fails in sub-module” but almost all of them describe the situation which violates the main rule of sub-module.

 

THE FIRST RULE OF SUB-MODULE:
YOU DO NOT CHANGE THE SUB-MODULE

 

So, if you want to change the content of the sub-module you need to alter it in its own repository, push the changes, and check out these changes in the sub-module location in the parent application. But I will tell you about this a bit later.

Now we decide to use the git sub-module to get our child module content in the needed state in our parent.

Firstly, we need to add the remote repository of the child application to our parent project:

git submodule add {remote.git.repo} modules/child

Here we define the location of our sub-module. We choose the “module” folder because this is the default play location for sub-projects.

After we just need to init and update our sub-module:

git submodule init

git submodule update

Congratulation!

Now you have your child application as a sub-module in the master state. In order to change the state we just go to modules/child and switch to the branch or tag that we want.

 

Rules for working with sub-modules:

  • Never change the content of sub-module. You will lose this change when you switch to another branch or tag.
  • If you work before on the branch without sub-module you will need to execute git sub-module init and git sub-module update
  • When you check out the branch with sub-module execute git sub-module update to set the state of sub-module to the correct state (after any sync with the remote repo)

 

Third step is “Reunion”

Reunion

The last thing we have to do is to define and configure our child project in the parent. First, we need to describe our child’s project in the build.sbt and set that our parent project depends on the child.

lazy val `parent` = (project in file(".")).enablePlugins(PlayScala).disablePlugins(PlayLayoutPlugin)

 .dependsOn(`child`)

 .aggregate(`child`)

lazy val `child` = (project in file("modules/hub")).enablePlugins(PlayScala).disablePlugins(PlayLayoutPlugin)

After this we can create an alias for the child’s routes in main.routes:

->       /child                                           child.Routes

And we can access all child’s resources by following the /child/{someroute}

 

Play application run with the following exception:

screen-shot-2016-11-04-at-22-54-00

  •  So, to resolve this problem, add -Dconfig.resource={child.conf} to run the configuration of your application.

 

Sometimes Intellij Idea tries to run the child project as root. To resolve this problem just ignore the child project and child-build project in the SBT tab.

 

If you need to debug into the child project, you will need to unignore the child project

SBT project

If you need help playing applications with sub-projects using git sub-modules, contact us. We’d be happy to support you.