Play Framwork 2.5 @Inject WSClient ws and @Inject CacheApi cache returns null

2,095 views
Skip to first unread message

goldfishhub

unread,
Mar 9, 2016, 12:52:19 AM3/9/16
to play-framework

I have a new project and I decided to test out the 2.5. 

However, I ran into the issue where I can't inject any Play components such as WSClient, CachAPI, Configuration, Environment etc.

I am always getting null for anything that I try to @Inject. 

Below is what I tried to do.

Am I doing something wrong?

import javax.inject.*;
import play.libs.ws.*;


public class MyComponent {
 
@Inject static WSClient ws;
 
@Inject static CacheApi cache;
 
@Inject static Play.api.Configuration config;
 
@Inject static Play.api.Environment env;


 
public static callSomething() {


 
if (ws == null) {
   
System.out.println("I have no ws!");
 
}
 
if (cache == null) {
   
System.out.println("I have no cache!");
 
}
 
if (config == null) {
   
System.out.println("I have no config!");
 
}
 
if (env == null) {
   
System.out.println("I have no env");
 
}


 
}
}


Thanks in advanced.

Greg Methvin

unread,
Mar 9, 2016, 1:03:29 AM3/9/16
to play-framework
You can't inject static members. The point of using DI is to avoid having to use statics.

Also, I'd recommend using constructor injection, since that will make your components easier to test.

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/60ca2cf4-ac4a-4ee8-82fc-16f074adcd3a%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Greg Methvin
Senior Software Engineer

goldfishhub

unread,
Mar 9, 2016, 1:43:56 AM3/9/16
to play-framework
Thanks Greg. That was it!

I am new to DI with Guice.

Is there an ordinary way of creating WSClient, Cache without using DI?

Seems using DI for components that needs to be used in static methods  becomes quite wordy with DI workarounds. 

As an example, if I wanted to grab City or State list information. These information are stored in database and served by an api from another application. So I do not wish to hit the service every time. 

Basically I wish to use cache.getOrElse() method. If I have the data in the cache return from cache, if I don't, hit the service. 

Suppose I want to have a class like this. What is the best way to implement this? Pardon my crude example. 


public class State {
     
public static List<String> getStates() {
           
return cache.getOrElse(...., ....);
     
}


}

Greg Methvin

unread,
Mar 9, 2016, 2:08:45 AM3/9/16
to play-framework
Actually the nice thing about DI is that every component has its dependencies declared (ideally in the constructor) so it's pretty easy to see how you would instantiate a component in isolation. You can instantiate an EhCacheApi directly from an EhCache instance for example.

I'm not sure what State is meant to represent, but if State is a typical model class, you would usually have some sort of DAO such as a StateRepository that has a method like getStates. That repository would declare a CacheApi and perhaps a Database dependency in its constructor. Then, of course, you could easily test your StateRepository while creating test implementations of your cache and/or your database to make sure everything works as expected.




For more options, visit https://groups.google.com/d/optout.

goldfishhub

unread,
Mar 9, 2016, 1:42:26 PM3/9/16
to play-framework
Great explanation! I see the value of DI and why Play moved towards it. 

I was asking the above question because I have a system that did not use the DI pattern. So if we do migration we will have to add classes such as StateRepository like you mentioned to introduce DI into the existing system. 

This will require more code, design changes and more time to migrate. I am not saying that it is not doable. It certainly is elegant to be able to decouple code like this through out the system. 

To make it easier on transitioning to 2.5 would you guys consider adding an alternative to @Inject in the tutorial in addition to pushing towards using @Inject. 

Like: 
WSClient ws = Play.current().injector().instanceOf(WSClient.class);

CacheApi cache = Play.current().injector().instanceOf(CacheApi.class);


Also the next big change, that is a bit of problem for people who were using WSClient's F.Promise.get(TIME_OUT) as blocking call in conjunction with Cache.getOrElse() using Callable interface implementation.

It seemed that the blocking call similar to F.Promise.get(TIME_OUT) is not there in CompletionStage<>. 

So now we have to create our own blocking call mechanism in Web API call, to be able to return data from Callable.call() method. Which means more code and more design changes. 

Would be nice if any future version of play preserve the functionality of older APIs in someway so users don't have to drastically change their design when trying to upgrade. 

Or at least provide guidelines for migrating api or functionality that no longer exists, 

Pardon my ignorance, if this is already described somewhere in the doc. I just can't seem to find it. 

Thanks in advanced. 

Greg Methvin

unread,
Mar 9, 2016, 3:09:12 PM3/9/16
to play-framework
I was asking the above question because I have a system that did not use the DI pattern. So if we do migration we will have to add classes such as StateRepository like you mentioned to introduce DI into the existing system. 

 For the transition, one thing you may want to consider is static injection.

To make it easier on transitioning to 2.5 would you guys consider adding an alternative to @Inject in the tutorial in addition to pushing towards using @Inject.

If you are looking for a way to still use static global state in some places, you can use static injection. But it would likely be too difficult to support yet another way of doing things. Another thing you can do is override the GuiceApplicationLoader and set up any static state you need there.
 
Also the next big change, that is a bit of problem for people who were using WSClient's F.Promise.get(TIME_OUT) as blocking call in conjunction with Cache.getOrElse() using Callable interface implementation.

It seemed that the blocking call similar to F.Promise.get(TIME_OUT) is not there in CompletionStage<>. 

You can still do it with CompletionStage by converting to CompleteableFuture, i.e. completionStage.toCompleteableFuture().get(). That said, you are probably better off making your APIs return CompletionStage instead of using blocking calls.  
 
Would be nice if any future version of play preserve the functionality of older APIs in someway so users don't have to drastically change their design when trying to upgrade.

You can also just to stay on the old version of Play. We still support old versions with security patches and critical bugfixes; we just don't usually add new features. I think we are approaching a point where we don't have to make major changes to how your application is structured, but it's hard to make promises there.

goldfishhub

unread,
Mar 9, 2016, 3:38:35 PM3/9/16
to play-framework
Thank Greg. Needless to say I am also very new to Java 8 concurrency library. :(

Is there any performance penalty in using dependency injection everywhere in the system? 

I may be asking too much from you with what I am about to ask. I understand if you choose not to answer.

I wanted to see how you would change the following code with returning CompletableFuture/CompletionStage. 

I can hear your sigh when you see static method again in the code. :) But if we already have this how would we transform this so we can return CompletableFuture/CompletionStage from Callable.call method implementation?

You might tell me to throw out the current implementation. :) That's a fair game too. But I am curious to see if someone designed something like this, how would they migrate with minimal effort. 

public class AppSettingManagement {


   
private static final int CACHE_EXPIRATION = 2 * 60 * 60; // 2 hours
   
private static final int TIMEOUT = 20000;


   
public static AppSettings getAppSetting(String appName) throws Exception {

       
CacheApi cache = Play.current().injector().instanceOf(CacheApi.class);
       
return cache.getOrElse(CacheNames.APP_SETTINGS + "_" + appName, new AppSettingsRetriever(appName),
                CACHE_EXPIRATION
); // expires in 2 hours
   
}


   
static class AppSettingsRetriever implements Callable<AppSettings> {
       
private String appName;


       
public AppSettingsRetriever(String appName) {
           
this.appName = appName;
       
}


       
@Override
       
public AppSettings call() throws Exception {
           
WebRequest request = new WebRequest();

            request
.put("appName", appName);
           
.... // add other credential stuffs

           
WSClient ws = Play.current().injector().instanceOf(WSClient.class);
           
WSRequest wsRequest = ws.url(ConfigSettings.getAppSettingsUrl())
                                   
.setHeader("Content-Type", "application/json")
                                   
.setRequestTimeout(TIMEOUT);


           
CompletableFuture<JsonNode> jsonPromise = (CompletableFuture<JsonNode>)wsRequest.post(Json.toJson(request))
                                                                                           
.thenApply(WSResponse::asJson);


           
WebResponse response = Json.fromJson(jsonPromise.get(), WebResponse.class);


           
if (response.isSuccess()) {
               
return (AppSettings) response.getResult();
           
}
           
else {
               
throw new Exception(response.getMessage());
           
}
       
}
   
}
}

Reply all
Reply to author
Forward
0 new messages