documentation/manual/working/javaGuide/main/http/JavaActionsComposition.md
This chapter introduces several ways to define generic action functionality.
Previously, we said that an action is a Java method that returns a play.mvc.Result value. Actually, Play manages internally actions as functions. An action provided by the Java API is an instance of play.mvc.Action. Play builds a root action for you that just calls the proper action method. This allows for more complicated action composition.
Here is the definition of the VerboseAction:
You can compose the code provided by the action method with another play.mvc.Action, using the @With annotation:
At one point you need to delegate to the wrapped action using delegate.call(...).
You also mix several actions by using custom action annotations:
Note: Every request must be served by a distinct instance of your
play.mvc.Action. If a singleton pattern is used, requests will be routed incorrectly during multiple request scenarios. For example, if you are using Spring as a DI container for Play, you need to make sure that your action beans are prototype scoped.
Note:
play.mvc.Security.Authenticatedandplay.cache.Cachedannotations and the corresponding predefined Actions are shipped with Play. See the relevant API documentation for more information.
By default, action composition is not applied when handling WebSockets. A guide how to enable action composition, including an example, can be found in the [[WebSockets documentation|JavaWebSockets#WebSockets-and-Action-composition]].
You can also mark action composition with your own annotation, which must itself be annotated using @With:
Your Action definition retrieves the annotation as configuration:
You can then use your new annotation with an action method:
You can also put any action composition annotation directly on the Controller class. In this case it will be applied to all action methods defined by this controller.
Note: If you want the action composition annotation(s) put on a
Controllerclass to be executed before the one(s) put on action methods setplay.http.actionComposition.controllerAnnotationsFirst = trueinapplication.conf. However, be aware that if you use a third party module in your project it may rely on a certain execution order of its annotations.
You can pass an object from an action to a controller by utilizing request attributes.
Then in an action you can get the request attribute like this:
To see in which order the actions of the action composition chain will be executed, please add the following to logback.xml:
<logger name="play.mvc.Action" level="DEBUG" />
You will now see the whole action composition chain with the according annotations (and their associated method/controller) in the logs:
[debug] play.mvc.Action - ### Start of action order
[debug] play.mvc.Action - 1. ...
[debug] play.mvc.Action - 2. ...
[debug] play.mvc.Action - ### End of action order
By default [[body parsing|JavaBodyParsers]] takes place before action composition happens, meaning you are able to access the already parsed request body inside every action's call(...) method via request.body(). However, there are use cases where it makes sense to defer body parsing after action composition took place. For example:
Of course, when deferring body parsing, the request body won't be parsed yet inside a call(...) method and therefore request.body() will return null.
You can enable deferred body parsing globally in conf/application.conf:
play.server.deferBodyParsing = true
Just be aware that, like all play.server.* config keys, this config won't be picked up by Play when running in DEV mode, but only in PROD mode. To set this config in DEV mode you have to set it in build.sbt:
PlayKeys.devSettings += "play.server.deferBodyParsing" -> "true"
Instead of enabling deferred body parsing globally, you can enable it just for specific routes by using the [[routes modifier|JavaRouting#The-routes-file-syntax]] deferBodyParsing:
+ deferBodyParsing
POST / controllers.HomeController.uploadFileToS3(request: Request)
The opposite is true as well. If you globally enable deferred body parsing you can disable it for specific routes by using the [[routes modifier|JavaRouting#The-routes-file-syntax]] dontDeferBodyParsing:
+ dontDeferBodyParsing
POST / controllers.HomeController.processUpload(request: Request)
You can use [[runtime Dependency Injection|JavaDependencyInjection]] or [[compile-time Dependency Injection|JavaCompileTimeDependencyInjection]]together with action composition.
For example, if you want to define your own result cache solution, first define the annotation:
@action-composition-dependency-injection-annotation
And then you can define your action with the dependencies injected:
@action-composition-dependency-injection
Note: As stated above, every request must be served by a distinct instance of your
play.mvc.Actionand you must not annotate your action as a@Singleton.
When using [[compile-time Dependency Injection|JavaCompileTimeDependencyInjection]], you need to manually add your Action supplier to JavaHandlerComponents. You do that by overriding method javaHandlerComponents in BuiltInComponents:
@action-composition-compile-time-di
Note: As stated above, every request must be served by a distinct instance of your
play.mvc.Actionand that is why you add ajava.util.function.Supplier<Action>instead of the instance itself. Of course, you can have aSupplierreturning the same instance every time, but this is not encouraged.