With the new Version 2.4/2.5 of the Play Framework they moved further towards injecting everything and removing the servers state. play.Play.application() is now deprecated. However, I need the application in my template (e.g. to get all supported languages displayed on all pages with play.i18n.Lang.availables(play.Play.application())).
I'm aware I could:
Pass play.Application explicitly to all of my templates.
Add an implicit parameter to my template like #()(implicit app: play.Application). However, in my Java-Project it's not really implicit, I have to pass it every time I render the template.
Create a Scala object providing the application implicitly. However, this also needs the deprecated play.api.Play.current.
How can I inject play.Application in my templates?
---- Update: ----
What I've tried so far, I created the following setup:
index.scala.html:
#(title: String)
#template(title) { //here is the play.Application obviously missing, however I don't want to pass it in every template - even worse in every Controller <-- this is the core of my question
Welcome to my page!
}
template.scala.html:
#(title: String)(content: Html)(implicit app: play.Application)
<html>
<head>
<title>#title</title>
</head>
<body>
<p>Is live? #app.isProd</p>
#content
</body>
</html>
Controller function:
public Result index() {
return ok(views.html.index.render("home"));
}
You can get an application with Play.current() or inject an application in controller like this is explained in this question. The template should get the argument of type play.Application.
It should be something like this.
The template, let's say is injectappexample.scala.html:
#(app: play.Application)
.... use the app object
The controller:
public class SomeController extends Controller {
Provider<Application> applicationProvider;
#Inject
public SomeController(Provider<Application> applicationProvider) {
this.applicationProvider = applicationProvider;
}
public Result injectAppExample() {
return ok(injectappexample.render(applicationProvider.get());
}
}
It worth to reconsider sending the application object to the template. If you should send a particular configuration property value, inject Configuration in the controller, get the value from configuration object and send it to the template. In this case injecting of application is not needed at all.
The template:
#(value: String)
.... use the value
The controller:
public class SomeController extends Controller {
Configuration configuration;
#Inject
public SomeController(Configuration configuration) {
this.configuration = configuration;
}
public Result injectAppExample() {
return ok(injectappexample.render(configuration.getString("SOME_PROPERTY"));
}
}
I just had to look into this for Play framework 2.6.x. It is possible to inject an object into a template according to the documentation: https://www.playframework.com/documentation/2.6.x/ScalaTemplatesDependencyInjection.
I implemented a simple sample (a bit contrived) and I used scala:
test.scala.html:
#this(configuration: play.api.Configuration)
#(key: String)
config = #configuration.get[Seq[String]](key).mkString(", ")
HomeController.scala
package controllers
import javax.inject._
import play.api._
import play.api.i18n._
import play.api.mvc._
/**
* This controller creates an `Action` to handle HTTP requests to the
* application's home page.
*/
#Singleton
class HomeController #Inject()(cc: ControllerComponents, testView: views.html.test) (implicit configuration: Configuration) extends AbstractController(cc) with I18nSupport{
def test() = Action { implicit request =>
Ok(testView("play.i18n.langs"))
}
}
routes:
GET /test controllers.HomeController.test()
The configuration object gets injected into views.html.test template and the view itself is injected into the controller. Note the #this(configuration: play.api.Configuration) statement in the template. Play will generate a class behind the template with a constructor that is injected the Configuration object.
Please note that the injected configuration into the controller doesn't have any role in this particular code. I experimented with other permutations before I found this solution ... Let's say that if you have an inner template used by an outer template called from the controller, and the inner template needs the configuration object you need to feed the configuration from the controller top down, and add an implicit configuration: play.api.Configuration parameter in all the templates in the hierarchy path right to the template that needs it, something like this: #(message: String)(implicit messagesProvider: MessagesProvider, configuration: play.api.Configuration)
. Then the controller injected configuration is fed to the top template all the way to the template that needs it.
It is generally discouraged to inject the Application itself, because that makes your code very cumbersome to test. Instead, think about what you actually need and inject that directly.
If you have a number of things you need in pretty much every template, my suggestion would be to create some sort of a context class that you can inject in your controller and then pass it on to the template.
First of all, though you are asking how to inject application what you really need is configuration.
You can do something like this in play 2.5. I'll show constructor injection you may use field injection as your requirement.
import com.google.inject.Inject;
import play.Configuration;
public class MyClass{
private Configuration conf;
#Inject
public MyClass(Configuration conf){
this.conf = conf;
}
}
Now you have your configuration class. Then specifically for your requirement posted in your comment, you can do this.
List<Object> langList = conf.getList("play.i18n.langs");
//conf.getList("") returns an object array.
//You can cast to Strings (assuming you are using Java8).
List<String> languages = new ArrayList<>(langList.size());
for (Object object : langList) {
languages.add(Objects.toString(object, null));
}
Now you can have your languages list in languages.
Related
I am trying to dive deep into the mechanics of the Spring Framework. The following piece of code achieves inserting "token" parameter from HTTP request into the token variable:
#POST
#Path("/")
Response saveOne(#HeaderParam("token") String token, UserDTO uDTO) {
}
How can I achieve the same?
And what tools do I need? Is it done using AOP? Say, I would like to come up with a custom annotation #MyAnnotation and any parameter or field marked by it would be customized by some logic. Example:
public class MyClass {
...
#MyAnnotation
private String myVar1;
public void myMethod(#MyAnnotation myParam1) {
...
}
...
}
When I create instance of MyClass or when I autowire it in my Spring application I would like to have a piece code that triggers right before I use the myVar1 variable and/or when I call the myMethod(param1). I want to set the annotated variable to whatever value I want. How can I do this?
What makes me wonder is how Spring does do it in case of the #HeaderParam? I basically need the same functionality.
I've created a lot of common small bean-definition containers (#Configuration) which I use to rapidly develop applications with Spring Boot like:
#Import({
FreemarkerViewResolver.class, // registers freemarker that auto appends <#escape etc.
ConfigurationFromPropertiesFile.class, // loads conf/configuration.properties
UtfContentTypeResponse.class, // sets proper Content-language and Content-type
LocaleResolverWithLanguageSwitchController // Locale resolver + switch controller
);
class MySpringBootApp ...
For example, one of such #Configurations can set up session storage for locale cookie with web controller to switch to selected language etc.
They are very fun to work with and reuse, but it would be really great to make it parametrized, which could allow lot more reusege. I mean something like:
Pseudo code:
#Imports( imports = {
#FreemarkerViewResolver( escapeHtml = true, autoIncludeSpringMacros = true),
#ConfigurationFromProperties( path = "conf/configuration.properties" ),
#ContentTypeResponse( encoding = "UTF-8" ),
#LocaleResolver( switchLocaleUrl = "/locale/{loc}", defaultLocale = "en"
})
So, I basically mean "configurable #Configurations". What would be the best way to make the configuration that way?
Maybe something more like this (again, pseudo code):
#Configuration
public class MyAppConfiguration {
#Configuration
public FreemarkerConfiguration freemarkerConfiguration() {
return FreemarkerConfigurationBuilder.withEscpeAutoAppend();
}
#Configuration
public ConfigurationFromPropertiesFile conf() {
return ConfigurationFromPropertiesFile.fromPath("...");
}
#Configuration
public LocaleResolverConfigurator loc() {
return LocaleResolverConfigurator.trackedInCookie().withDefaultLocale("en").withSwitchUrl("/switchlocale/{loc}");
}
Let me quote Spring Boot Reference Guide - Externalized Configuration:
"Spring Boot allows you to externalize your configuration so you can work with the same application code in different environments."
In my opinion the customization is not done at import time via annotation parameters like in your 2nd pseudo code block, instead the customization happens at run time e.g. in the configuration classes. Let me adapt your 3rd code block (only one function):
#Configuration
public class MyAppConfiguration {
#Autowired
private Environment env;
// Provide a default implementation for FreeMarkerConfigurer only
// if the user of our config doesn't define her own configurer.
#Bean
#ConditionalOnMissingBean(FreeMarkerConfigurer.class)
public FreeMarkerConfigurer freemarkerConfig() {
FreeMarkerConfigurer result = new FreeMarkerConfigurer();
result.setTemplateLoaderPath("/WEB-INF/views/");
return result;
}
...
#Bean
public LocaleResolverConfigurator loc() {
String defaultLocale = env.getProperty("my.app.config.defaultlocale", "en");
String switchLocale = env.getProperty("my.app.config.switchlocale", "/switchlocale/{loc}");
return LocaleResolverConfigurator.trackedInCookie().withDefaultLocale(defaultLocale).withSwitchUrl(switchLocale);
}
For LocaleResolverConfigurator the configuration is read from the environment, meaningful default values are defined. It is easy to change the default value(s) by providing a different value for a config parameter in any of the supported ways (documented in the first link) - via command line or a yaml file. The advantage over annotation parameters is that you can change the behavior at run time instead of compile time.
You could also inject the config parameters (if you prefer to have them as instance variable) or use a lot of other conditions, e.g. #ConditionalOnMissingBean, #ConditionalOnClass, #ConditionalOnExpression and so on. For example with #ConditionalOnClass you could check if a particular class is on your class path and provide a setting for the library identified by this class. With #ConditionalOnMissingClass you could provide an alternative implementation. In the example above I used ConditionalOnMissingBean to provide a default implementation for the FreeMarkerConfigurer. This implementation is only used when no FreeMarkerConfigurer bean is available thus can be overridden easily.
Take a look at the starters provided by Spring Boot or the community. A good read is also this blog entry. I learned a lot from spring-boot-starter-batch-web, they had an article series in a German Java magazine, but parts are also online, see Boot your own infrastructure – Extending Spring Boot in five steps (MUST READ) and especially the paragraph "Make your starter configurable by using properties".
Though I like the idea of having imports be parameterized, I think that as it stands now using #Import and #Configuration not a good fit.
I can think of two ways to use dynamic configurations, that don't rely on PropertySource style configuration.
Create a custom #ImportConfig annotation and annotation processor that accepts configuration properties that are hard-coded into the generated source files.
Use a BeanFactoryPostProcessor or BeanPostProcessor to add or manipulate your included beans respectively.
Neither is particularly simple IMO, but since it looks like you have a particular way of working. So it could be worth the time invested.
I have a requirement of compatibility with a new architecture based in Spring 3.2. The requirement is: a http request will come to a controller with an attribute that defines which kind of object is required. For example ...mycontroller/load?objType='obj1'.
My controller will have this structure:
#Controller
public class myController{
private ObjectService objectService;
#Autowired
public setObjectService(ObjectService objectService){
this.objectService = objectService;
}
}
So after that, I need to check this attribute to decide which service will I use. For example, this case is Obj1Service (method: "load"). All this services are extended from ObjectService, so: It is a good idea to swap objectService dependency to Obj1Service / Obj2Service in each incoming call? For example:
if(objType.equals("obj1")) this.setObjectService(context.getBean("obj1Service"..))
if(objType.equals("obj2")) this.setObjectService(context.getBean("obj2Service"..))
I know that is not a great design, but we need to integrate this new modules with other system that produces this kind of http requests.
It is necessary the inheritance because we have very similar behavior in many service's code, but with modifications in internal methods, so part of the behavior will be placed in ObjectService (it is not abstract) and other portion of the code will be placed in it children. Is there another way, more appropriate, to do this? Or you consider that is an acceptable solution?
Thanks!
You can use a map for all of your services, and get the appropriate service inside each controller method.
Let's say you have two services:
#Service("obj1")
public class ObjectServiceImpl1 implements ObjectService {
...
}
#Service("obj2")
public class ObjectServiceImpl2 implements ObjectService {
...
}
In your controller:
private Map<String, ObjectService> objectServices;
#Autowired
public setObjectServices(Map<String, ObjectService> objectServices){
this.objectServices= objectServices;
}
Spring will inject the map with all the ObjectService beans mapped by their names.
In load?objType=obj1 handler, you would have something like:
objectServices.get("obj1").doSomething(); // will use ObjectServiceImpl1
The same with load?objType=obj2 handler:
objectServices.get("obj2").doSomething(); // will use ObjectServiceImpl2
And so on.
I'm a complete newb at this so I apologize in advance. I'm trying to create
an OSGi component that simply shows a hello world message and is configurable via the input from felix. Then spits it out on a jsp page. I'm using scr annotations to help do this. Here is my java code
package com.training.cq5.trainingApp;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.component.ComponentContext;
import org.apache.sling.commons.osgi.PropertiesUtil;
#Component(label= "Welcome Message",
description = "Welcome Message for the training excercise",
immediate = true, enabled = true, metatype=true)
#Properties({
#Property(name = "welcome.message", value = "WelcomeMessage")
})
#Service(WelcomeMessage.class)
public class WelcomeMessage {
private static String welcome_message = "Welcome";
#Activate
protected void activate(ComponentContext ctx) {
welcome_message = PropertiesUtil.toString(ctx.getProperties().get(welcome_message), welcome_message);
}
public static String getMessage() {
return welcome_message;
}
}
Here is were I am calling it in the JSP:
<%# page import="com.training.cq5.trainingApp.WelcomeMessage" %>
<h2><%= WelcomeMessage.getMessage() %></h2>
Is there any reason why it's not updating from felix? All I'm getting is the "Welcome"
text from the welcome_message string.
You are accessing WelcomeMessage.getMessage() as a static method, but what you want is the actual service. When you annotate a class with the #Service and #Component annotation, you indicate to the OSGI framework that you want an instance of this class registered as a service. This service instance is managed by the OSGI framework, in terms of its lifecycle (when its instantiated) or through which classloader the appropriate classes are loaded.
However in order to use the #Component and #Service annotations, you'll have to use the Apache Felix SCR plugin. Once that works, your service will be instantiated.
Then you'll have to access the service. The easiest way in Sling, which you appear to be using, is SlingScriptHelper.getService() which lets you lookup a service.
Update
In OSGI services are registered by their type. When you declare a service with #Service(MyClass.class), the service will be registered under the type MyClass. To retrieve it you would query the service registry for a service of the given type. In Java code you'd be using either getServiceReference(Class clazz)/getService(ServiceReference reference) the #Reference annotation.
In a JSP on a Sling system you can use the SlingScriptHelper, as outlined earlier. Here's a short code sample (assuming correct imports):
<%
SlingBindings bindings = (SlingBindings) req.getAttribute(SlingBindings.class.getName());
SlingScriptHelper scriptHelper = bindings.getSling();
MyService service = scriptHelper.getService(MyService.class);
// ... do stuff with service.
%>
If you are going to work more with OSGI, I highly recommend the OSGI specification. It's free to download and explains everything in great detail.
ilikeorangutans is correct that you don't want a static method on your OSGi service - the idea is that a service implements an interface, clients retrieve it from their OSGi context and use it via its service interface.
The Apache Sling webloader sample uses this technique to access a Webloader service in its request processing scripts. The scripts are ESP in this case (server-side javascript) but the principle is exactly the same with JSP.
The service interface is defined in Webloader.java, and WebLoaderImpl.java implements it as an OSGi service.
Then, the html.esp script gets the service using sling.getService:
var loader = sling.getService(Packages.org.apache.sling.samples.webloader.Webloader);
Change this line:-
welcome_message = PropertiesUtil.toString(ctx.getProperties().get(welcome_message), welcome_message);
to
welcome_message = PropertiesUtil.toString(ctx.getProperties().get("welcome.message"), welcome_message);
notice the difference :-ctx.getProperties().get(welcome_message) vs ctx.getProperties().get("welcome.message")
I am using Spring, here is a controller:
#Controller
public class PersonController {
#Resource(name="PersonService")
private PersonService personService;
#RequestMapping(value = "/Person", method = RequestMethod.GET)
public String getPersons(Model model) {
// Retrieve all persons by delegating the call to PersonService
List<Person> persons = personService.getAll();
// Attach persons to the Model
model.addAttribute("persons", persons);
//then return to view jsp
}
and here is a service :
#Service("personService")
#Transactional
public class PersonService {
public List<Person> getAll() {
//do whatever
}
}
However, to properly make use of DI I should change the controller to make use of an interface (?) like so:
#Controller
public class PersonController {
#Resource(name="personService")
private IPersonService personService; //Now an interface
}
This would then allow me, for example, to use two services one test and one live. Which I could alter by adding/removing the annotation on the services :
#Service("personService") // this line would be added/removed
#Transactional
public class LivePersonService implements IPersonService {
public List<Person> getAll() {
//do whatever
}
}
and
#Service("personService") //this line would be added/removed
#Transactional
public class TestPersonService implements IPersonService {
public List<Person> getAll() {
//do something else
}
}
However one of the main benefits is lost due to the fact that the code has to be recompiled ? Whereas if I used xml lookup I could alter the dependency on-the-fly ?
The configuration is still external, because it is outside where you define which implementation is going to be injected. Inside the class, you just hardcode the "name" of something the class depends on (which is ok, because this dependency is inherent to the class).
This said, you can use XML to override the annotations of your code for the tests execution (you would have a specific XML application context for your tests) and specify which implementation you will inject.
Therefore, you don't need to change your code to run the tests. Take a look to this answer.
Well that's correct. Annotations are configuration inside the source code. Mainly intended when you have one class for each service. If you have more than one implementation for a particular interface, then XML will be a better option. Also you can mix XML configutation with annotations.
The conventional way, that I heard last time from DI camp, is that in unit tests, you shouldn't use the DI framework. Rather, simply instantiate mock service yourself and set it to the host object
test()
PersonController contr = new PersonController();
contr.personService = new TestPersonService();
// testing contr
This was hailed as the first and major achievement of DI, much to the puzzlement of people (like me) who don't get it. See my previous criticisms: advantage of using applicationcontext.getbean vs #configurable
If the DI supporters in this thread reflect the new trend in DI camp, they no longer do unit tests that way; instead tests depend on DI too, with a test specific DI config. Then it's really no different from service locator pattern. If the major feature of DI is moot, then what's the point?
Your controller class perfectly illustrated that. It cannot be used outside Spring DI framework as a POJO. There's nothing POJO about it. And nobody cares, rightfully. Same thing if your class depends on a service locator framework.
There are other features provided by Spring beans framework, none of them depends on DI; they can be implemented in a service locator framework just as well. Many people when defending DI the design pattern, are actually defending Spring the entire stack. You can actually use Spring as a service locator framework; Spring will not advertise this now, it's a blow to its main hype point; but it will once the hype weakens and it must appeal to the doubters.