post-photo

Play framework modularization: everything you wish you had been told

There are many tutorials and articles describing basic module functionality and how you can build it. These articles sometimes leave blanks of the needed information, so this post will do an in-depth tour of the topic applied in the Play framework.

text

What is a module?

In Play framework the modules are small functionality packages. These can be easily attached to some project, encapsulating specific functionality.This makes them reusable, but this is why these need more careful design when writing. If you are familiar with the clean code principles you know exactly why these are good for us :)

If you want to build a new module your objective will fall to two categories;

  1. you want to build some separate functionality like a function library, or
  2. you want to build your webapp from shards, and you want a subpage like module.

The library functionality can be separated into two more parts,

  1. you want to build a basic library or
  2. you want to build something that needs to onStart and onStop; we call them plugins.

How to start?

First of all you need a folder where you can create your module, and a sample “how to use” play app to test it. I will use and as space-holders below to clarify things. I ‘m expecting you to have the activator in your path, and you to have basic knowledge of using a terminal. (I’m using windows, with a cmder terminal emulator, it is possible your terminal will not work out of the box with these commands.) I will use play java in the codes below, but the scala version is nearly the same.

> mkdir <module>
> cd <module>
> activator new <module>
  play-java-2.3
> activator new <sample>
  play-java-2.3

If you want scala or 2.4 modules simply write play-scala or play-java or play-scala-2.3 instead of play-java-2.3 .

First of all, we extend our module’s build.sbt:

add to build.sbt

organization := "my.org"
organizationName := "My Org Name"
organizationHomepage := Some(new URL("http://my.org"))

It is a custom to mark the writer/organization of the modules.

Secondly, we configure our sample’s build.sbt:

add to build.sbt

lazy val module = RootProject(file("../<module>"))
lazy val root = (project in file(".")).enablePlugins(PlayJava).dependsOn(module)

If you look carefully, you will notice that this must be inserted somewhere on the top of the file, because a root is defined by default. The first line is defining a new variable with the root of your module, and the second injects it with the dependsOn(module) call. (Yes, you do not need to insert the second line, it’s enough just paste the dependsOn after it.)

Now you can start your favourite IDE and start the actual programming. \o/

Notes for before you start coding

There are some principles to which you should pay attention. First of all, this module you start writing must be easily usable and it must be totally independent. Try to avoid connecting to databases, using models (which are defined by you) from the host application. Instead, try defining traits or interfaces. Let the user implement the save functions, and don’t try to force him to use your preferred db engine or name conventions. The less dependencies kept in your module, the better. Try to lay how you want to use this module down, and design your module with this very mindset.

IDEs can be your best friends and worst enemies too. I’m using IntelliJ. It has some good points, but can make really silly errors when you try to use it with sbt. I’ll try to summarize how to start a fresh project in IntelliJ after you crated it with the activator.

After that you have 2 projects in the project tree. Now we are ready to start coding!

Basic Library type modules

With these types of modules you don’t have so much to do.

First of all, you need to clean it:

clean module folder

> cd <module>
> rm -Rf conf/* public/* test/* app/controllers app/views

And write some code!

For example:

just write it

package src;
import play.Logger;
import play.Play;
public class WriterTest {
   public static void writeToLog() {
      String prop = Play.application().configuration().getString("test.str");
      prop = ((prop == null) ? "nothing interesting in the properties :(" : prop);
      Logger.info(prop);
   }
}

In the sample application you can call it easily. That’s all!

If you read this code carefully you can see, that we are reading the sample application properties file in there. So basically there are no limitations, all your static libraries can easily be separated from your code to modules. This is how you can make them more reusable.

Plugin type modules

So in play framework 2.3 the official documentation used this term for modules that have states besides your webapp. Versions 2.3 and 2.4 are different in this section, so I will post my base logic here, and then show how you can make it work on 2.3 and 2.4. The scala and java versions are still close enough to show only the less natural (java) way, it’s easy to port to scala. So we will do a basic exchange rate downloader.

My first class is do the download and parse things:

Exchanger.java

public class Exchanger {
   private String base;
   private String target;
   private List<Double> history;
   public void doUpdate() {
      //http://stackoverflow.com/a/15474477/2118749 🙂
      AsyncHttpClientConfig conf = new AsyncHttpClientConfig.Builder().build();
      WSClient client = new NingWSClient(conf);
      WSResponse response = client.url("https://www.google.com/finance/converter?a=1&from=" + base + "&to=" + target).get().get(3000, TimeUnit.MILLISECONDS);
      String ratestr = (((response.getBody()).split("bld>"))[1]).split(target)[0];
      double rate = Double.parseDouble(ratestr);
      history.add(rate);
   }
   public List<Double> getHistory() {
      return history;
   }
   public Exchanger(String base, String target) {
      this.base = base;
      this.target = target;
      this.history = new ArrayList<Double>();
   }
}

It is going to the googleconverter, and save the current exchange rate to a list.

We need a runnable to schedule it:

RefresherJob.java

public class RefresherJob implements Runnable {
   private final Exchanger exchanger;
   public RefresherJob(Exchanger exchanger) {
      super();
      this.exchanger = exchanger;
   }
   @Override
   public void run() {
      try {
         exchanger.doUpdate();
      } catch(Exception e) {
         Logger.error("There is a problem with the currencies!", e);
      }
   }
}

We need some more logic to initialize and start a scheduler:

ExchangerPlugin.java

public class ExchangerPlugin {
   private final static long refreshInterval = 300000L;
   private static Exchanger exchanger;
   public static List<Double> getHistory() {
      if(exchanger != null)
         return exchanger.getHistory();
      else
         return new ArrayList<>();
   }
   private void onStart() {
      exchanger = new Exchanger("USD", "EUR");
      exchanger.doUpdate();
      RefresherJob job = new RefresherJob(exchanger);
      Akka.system().scheduler().schedule(
            FiniteDuration.create(0, TimeUnit.MILLISECONDS),
            FiniteDuration.create(this.refreshInterval, TimeUnit.MILLISECONDS),
            job,
            Akka.system().dispatcher()
      );
   }
}

This is basicly our business logic. Now we need to wrap it up!

Play framework 2.3

In 2.3

modified ExchangerPlugin.java

public class ExchangerPlugin extends Plugin {
    ...
    public ExchangerPlugin(Application application) {
    }
    ...
    @Override
    public void onStart() {
        ...
    }
}

After these modifications, we can use it in a sample project. Add a new line to your conf/play.plugins (create a file if it doesn’t exist) with a number:namespace.class format. In our example:

conf/play.plugins

10000:src.ExchangerPlugin

The number is defining the load order of the plugins. (That is the main reason why this method gets the deprecated flag.)

Play framework 2.4

In 2.4 we have more things to do…

We need to make a new class (ExchangerModule):

ExchangerModule.java

import play.api.Configuration;
import play.api.Environment;
import play.api.inject.Binding;
import play.api.inject.Module;
import scala.collection.Seq;
 
public class ExchangerModule extends Module {
      public Seq<Binding<?>> bindings(Environment environment, Configuration configuration) {
         return seq(
               bind(IExchangerPlugin.class).to(ExchangerPlugin.class)
         );
      }
}

We need a new empty interface (You can skip this step but I think it’s the proper way due to the documentation. For a non Interface example see this):

IExchangerPlugin

public interface IExchangerPlugin {
}

And we need to modify our ExchangerPlugin:

modified ExchangerPlugin.java

@Singleton
public class ExchangerPlugin implements IExchangerPlugin {
    ...
    @Inject
    public ExchangerPlugin(ApplicationLifecycle lifecycle) {
        onStart();
        lifecycle.addStopHook(() -> {
            // previous contents of Plugin.onStop
            return F.Promise.pure(null);
        });
    }
}

And if you want to use it in your project you need to add it to your application.conf like this:

application.conf

play.modules.enabled += "src.ExchangerModule"

And inject somewhere to your Controllers:

example to inject

@Inject
ExchangerPlugin exchanger;

For more information on why it’s changed between versions, see the offical Module/Plugin migration guide.

SubPage modules and applications

For this we will make a new structure (It is theoretically possible to make fully separated subpage modules work together, but it’s more common to use subpage modules only in one root application.):

our subpage configuration

multi-module-project
    |- app
    |- conf
    |- modules
    |   |-module1
    |   |-module2
    |- project
    ...

build.sbt-s: mullti-module-project/build.sbt

lazy val root = (project in file(".")).enablePlugins(PlayJava)
  .dependsOn(module1,module2)
  .aggregate(module1,module2)
 
lazy val module1= project.in(file("modules/module1"))
  .enablePlugins(PlayJava)
 
lazy val module2= project.in(file("modules/module2"))
  .enablePlugins(PlayJava)

In the modules build.sbt just delete (or comment out) the lazy val root line.

routes:

multi-module-project/conf/routes

# Home page
GET        /                    controllers.Application.index()
->         /module1              module1.Routes
->         /module2              module2.Routes
# Map static resources from the /public folder to the /assets URL path
GET        /assets/*file        controllers.Assets.at(path="/public", file)

module1/conf/module1.routes

# Home page
GET        /                    controllers.module1.Application.index()
# Map static resources from the /public folder to the /assets URL path
GET        /assets/*file        controllers.module1.Assets.asset(path="/public", file)

Controllers:

At your modules, you need to use other namespaces! So if you want special module scope assets, you need to define a new asset builder, too . There are some annoying bugs with the namespaces + default asset builder so you need to make your module a dependent asset handler.

Assets.java

public class Assets extends Controller {
   public static controllers.AssetsBuilder delegate = new controllers.AssetsBuilder();
   public static Action<AnyContent> asset(String path, String file) {
      return delegate.at(path, file, false);
   }
}

You could use it in routes like above in the module1.routes example, and can use it in views like below in the view examples :)

Views in modules:

views example

<script src="@controllers.module1.routes.Assets.asset("javascripts/hello.js")" type="text/javascript"></script>

There are some drawbacks with this type of a module. First of all it’s not documented well enough; you need to do your personal research (possibly, we will create a more in-depth submodule blog post because of this). Secondly, they are more like logical bricks that can separate your huge codebase, and less like some lego pieces which are pluggable to nearly every spaceship you made from lego previously.

Offical subpage reference.

How to publish?

If you made the best plugin or library like module, you want to show it to others too – so how can you publish it?

Publish your module to locale first (from the module dir):

publish command consolte

> activator clean publish-local

You will see something like this on your output:

output

[info] Done packaging.
[info]  published module_2.11 to C:\Users\<user>\.ivy2\local\module\module_2.11\1.0-SNAPSHOT\poms\module_2.11.pom
[info]  published module_2.11 to C:\Users\<user>\.ivy2\local\module\module_2.11\1.0-SNAPSHOT\jars\module_2.11.jar
[info]  published module_2.11 to C:\Users\<user>\.ivy2\local\module\module_2.11\1.0-SNAPSHOT\srcs\module_2.11-sources.jar
[info]  published module_2.11 to C:\Users\<user>\.ivy2\local\module\module_2.11\1.0-SNAPSHOT\docs\module_2.11-javadoc.jar
[info]  published ivy to C:\Users\<user>\.ivy2\local\module\module_2.11\1.0-SNAPSHOT\ivys\ivy.xml
[success] Total time: 5 s, completed 03-Feb-2016 11:04:53

You can try it out with the sample application:

build.sbt modification

version := "1.0-SNAPSHOT"
-lazy val module = RootProject(file("../module"))
-lazy val root = (project in file(".")).enablePlugins(PlayJava).dependsOn(module)
+
+resolvers += "Local Play Repository" at "file://C:/Users/<user>/.ivy2/local"
+
+lazy val root = (project in file(".")).enablePlugins(PlayJava)
 scalaVersion := "2.11.6"
 libraryDependencies ++= Seq(
+       "module" % "module_2.11" % "1.0-SNAPSHOT",
        javaJdbc,
        cache,
        javaWs

If you want to publish it for a larger user base try this.

Have fun!

member photo

His precision is only coupled by his attention to detail. (Really.) He is passionate about becoming more and more effective in software development and loves experimenting with new technologies.

Latest post by Gergő Törcsvári

Learning by Doing – BlockChain & Akka Tutorial