I Have this class:
package ds;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import play.mvc.Http;
public class MyRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected String determineCurrentLookupKey() {
return Http.Context.current().session().get("currentDB");
}
}
But when I want to access into the Play session I have this error:
Caused by: java.lang.RuntimeException: There is no HTTP Context available from here.
at play.mvc.Http$Context.current(Http.java:34) ~[play_2.10-2.3.10.jar:2.3.10]
at play.mvc.Controller.session(Controller.java:72) ~[play_2.10-2.3.10.jar:2.3.10]
I also tried with HttpExecution.defaultContext() and his HttpExecutionContext but it cannot be cast to the Http.Context that is what I need.
I was thinking about get the request header but certainly I don't know how to handle it from my class and determine the session from the request
You can't access the Play Context in that tier cause is out of scope. In the documentation of the AbstractRoutingDatasource:
The main description of the class says:
Abstract DataSource implementation that routes getConnection() calls to one of various target DataSources based on a lookup key. The latter is usually (but not necessarily) determined through some thread-bound transaction context.
So this class is suggesting that a way to get information of the current context should be using a Thread-Bound Transaction Context.
Now, Is PlayFramework Thread Context Safe? Reading Play documentation:
https://www.playframework.com/documentation/2.3.x/ThreadPools#Java-thread-locals
Java code in Play uses a thread local to find out about contextual information such as the current HTTP request. Scala code doesn’t need to use thread locals because it can use implicit parameters to pass context instead. Threads locals are used in Java so that Java code can access contextual information without needing to pass context parameters everywhere.
So, if you are using a Java implementation, you can use ThreadLocals as context channel between components. Be careful if you create your own thread pools because there's a warning in the same documentation:
The default objects wrap the default user thread pool. If you want to do your own threading then you should use the HttpExecution class’s helper methods to get an ExecutionContextExecutor object yourself.
But that won't be a problem if you are not using custom thread pool in your app.
Said that, what you have to do is:
Define a ThreadLocalContext for the object that you want to use as router.
Put in the context. (You can do it in the controller, in the security controller if you are using an authorization framework like Deadbolt or even implementing a new request filter.)
Reading in the AbstractRoutingDataSource the ThreadLocal context.
Important! Don't forget to clean the Thread-Local or you can face a memory Leak.
Step 1:
public class RequestContext {
private static final ThreadLocal<String> contextHolder =
new ThreadLocal<String>();
public static void setRoutingKey(String key) {
contextHolder.set(key);
}
public static String getRoutingKey() {
return (String) contextHolder.get();
}
public static void clearRoutingKey() {
contextHolder.remove();
}
}
Step 2:
//Demostrative code, not tested, not even compiled
public static void myController() {
RoutingContext.setRoutingKey(Play.Context.request());
return bla;
}
Step 3:
@Override
protected Object determineCurrentLookupKey() {
String datasource = RoutingContext.getRoutingKey();
RoutingContext.clearRoutingKey();
return datasource;
}
Regards!
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments