I am using Guice's RequestScoped and Provider in order to get instances of some classes during a user request. This works fine currently. Now I want to do some job in a background thread, using the same instances created during request.
However, when I call Provider.get(), guice returns an error:
Error in custom provider, com.google.inject.OutOfScopeException: Cannot
access scoped object. Either we are not currently inside an HTTP Servlet
request, or you may have forgotten to apply
com.google.inject.servlet.GuiceFilter as a servlet
filter for this request.
afaik, this is due to the fact that Guice uses thread local variables in order to keep track of the current request instances, so it is not possible to call Provider.get() from a thread different from the thread that is handling the request.
How can I get the same instances inside new threads using Provider? It is possible to achieve this writing a custom scope?
I recently solved this exact problem. There are a few things you can do. First, read up on ServletScopes.continueRequest(), which wraps a callable so it will execute as if it is within the current request. However, that's not a complete solution because it won't forward #RequestScoped objects, only basic things like the HttpServletResponse. That's because #RequestScoped objects are not expected to be thread safe. You have some options:
If your entire #RequestScoped hierarchy is computable from just the HTTP response, you're done! You will get new instances of these objects in the other thread though.
You can use the code snippet below to explicitly forward all RequestScoped objects, with the caveat that they will all be eagerly instantiated.
Some of my #RequestScoped objects couldn't handle being eagerly instantiated because they only work for certain requests. I extended the below solution with my own scope, #ThreadSafeRequestScoped, and only forwarded those ones.
Code sample:
public class RequestScopePropagator {
private final Map<Key<?>, Provider<?>> requestScopedValues = new HashMap<>();
#Inject
RequestScopePropagator(Injector injector) {
for (Map.Entry<Key<?>, Binding<?>> entry : injector.getAllBindings().entrySet()) {
Key<?> key = entry.getKey();
Binding<?> binding = entry.getValue();
// This is like Scopes.isSingleton() but we don't have to follow linked bindings
if (binding.acceptScopingVisitor(IS_REQUEST_SCOPED)) {
requestScopedValues.put(key, binding.getProvider());
}
}
}
private final BindingScopingVisitor<Boolean> IS_REQUEST_SCOPED = new BindingScopingVisitor<Boolean>() {
#Override
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
return scopeAnnotation == RequestScoped.class;
}
#Override
public Boolean visitScope(Scope scope) {
return scope == ServletScopes.REQUEST;
}
#Override
public Boolean visitNoScoping() {
return false;
}
#Override
public Boolean visitEagerSingleton() {
return false;
}
};
public <T> Callable<T> continueRequest(Callable<T> callable) {
Map<Key<?>, Object> seedMap = new HashMap<>();
for (Map.Entry<Key<?>, Provider<?>> entry : requestScopedValues.entrySet()) {
// This instantiates objects eagerly
seedMap.put(entry.getKey(), entry.getValue().get());
}
return ServletScopes.continueRequest(callable, seedMap);
}
}
I have faced the exact same problem but solved it in a different way. I use jOOQ in my projects and I have implemented transactions using a request scope object and an HTTP filter.
But then I created a background task which is spawned by the server in the middle of the night. And the injection is not working because there is no request scope.
Well. The solutions is simple: create a request scope manually. Of course there is no HTTP request going on but that's not the point (mostly). It is the concept of the request scope. So I just need a request scope that exists alongside my background task.
Guice has an easy way to create a request scope: ServletScope.scopeRequest.
public class MyBackgroundTask extends Thread {
#Override
public void run() {
RequestScoper scope = ServletScopes.scopeRequest(Collections.emptyMap());
try ( RequestScoper.CloseableScope ignored = scope.open() ) {
doTask();
}
}
private void doTask() {
}
}
Oh, and you probably will need some injections. Be sure to use providers there, you want to delay it's creation until inside the created scope.
Better use ServletScopes.transferRequest(Callable) in Guice 4
Related
How can I run code in my #RunWith(SpringRunner.class) #SpringBootTest(classes = {...}) JUnit test before Spring starts?
This question has been asked several times (e.g. 1, 2) but was always "solved" by some configuration recommendation or other, never with a universal answer. Kindly don't question what I am about to do in that code but simply suggest a clean way to do it.
Tried so far and failed:
Extend SpringJUnit4ClassRunner to get a class whose constructor can run custom code before initializing Spring. Failed because super(testClass) must be called first thing and already does a whole lot of things that get in the way.
Extend Runner to get a class that delegates to SpringRunner instead of inheriting it. This class could run custom code in its constructor before actually instantiating the SpringRunner. However, this setup fails with obscure error messages like java.lang.NoClassDefFoundError: javax/servlet/SessionCookieConfig. "Obscure" because my test has no web config and thus shouldn't meddle with sessions and cookies.
Adding an ApplicationContextInitializer that is triggered before Spring loads its context. These things are easy to add to the actual #SpringApplication, but hard to add in Junit. They are also quite late in the process, and a lot of Spring has already started.
One way to do it is to leave out SpringRunner and use the equivalent combination of SpringClassRule and SpringMethodRule instead. Then you can wrap the SpringClassRule and do your stuff before it kicks in:
public class SomeSpringTest {
#ClassRule
public static final TestRule TestRule = new TestRule() {
private final SpringClassRule springClassRule =
new SpringClassRule();
#Override
public Statement apply(Statement statement, Description description) {
System.out.println("Before everything Spring does");
return springClassRule.apply(statement, description);
}
};
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Test
public void test() {
// ...
}
}
(Tested with 5.1.4.RELEASE Spring verison)
I don't think you can get more "before" than that. As for other options you could also check out #BootstrapWith and #TestExecutionListeners annotations.
Complementing jannis' comment on the question, the option to create an alternative JUnit runner and let it delegate to the SpringRunner does work:
public class AlternativeSpringRunner extends Runner {
private SpringRunner springRunner;
public AlternativeSpringRunner(Class testClass) {
doSomethingBeforeSpringStarts();
springRunner = new SpringRunner(testClass);
}
private doSomethingBeforeSpringStarts() {
// whatever
}
public Description getDescription() {
return springRunner.getDescription();
}
public void run(RunNotifier notifier) {
springRunner.run(notifier);
}
}
Being based on spring-test 4.3.9.RELEASE, I had to override spring-core and spring-tx, plus javax.servlet's servlet-api with higher versions to make this work.
I am working on a project with jsf 2.2 on the web side and spring 4 on the business side. I have a web filter which receives a parameter from the request url. From this parameter I have to connect to a database. There are cases where there are different databases possible, so depending on the parameter I have to initiate a different database connection. The web filter looks like this:
#Component
public final class SecurityFilter implements Filter
{
#Autowired
private CommonEao commonEao;
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest)request;
String instance = req.getParameter("instance");
//I would like to have something here like:
// springContext.addParameter("instance", instance);
String company = req.getParameter("company");
commonEao.getConfiguration(company);
... Do stuff
}
}
How does it works? The commonEao contains methods to make queries to the database (JPA/Eclipselink...). At initialization, no entityManager is present in commonEao since it is injected in SecurityFilter before the doFilter is executed when an url is requested. After the url is requested, the instance of the database to use is known through the 'instance' request parameter.
When the method commonEao.getConfiguration(company) is invoked, the first thing that should happen is to create an entity Manager:
#Repository
public final class CommonEao extends AbstractEao
{
public final void getConfiguration(final String company)
{
if (entityManager == null)
{
//I would like to have something here like:
// String instance = springContext.getParameter("instance");
createEntityManager(instance);
}
else ...
}
}
As you can see, when the first time the url is requested, no entityManager exists and it needs to be created based on the instance name provided by the request. Based on the instance name the properties files containing database connection parameters will be used the call the Persistence.createEntityManagerFactory functionality... etc etc... :)
What is the idea? The idea that I had, as you can see in my comments, is to put a parameter in some global context/container that is available for all Spring beans. This idea comes from the JSF world, where you can create a managed bean, annotate it with applicationscope, define a variable in it, and access this variable from any jsf managed bean through injecting the application scoped bean with the managedproperty annotation:
#ManagedBean
public final class SomeJsfBean
{
#ManagedProperty(value = "#{applicationBean}")
private ApplicationBean applicationBean;
private void method()
{
applicationBean.setInstanceName("instance");
}
}
#ManagedBean
public final class AnotherJsfBean
{
#ManagedProperty(value = "#{applicationBean}")
private ApplicationBean applicationBean;
private void method()
{
String instance = applicationBean.getInstanceName();
}
}
I have some restrictions though. I want to use a global object of Spring. I don't have any XML config in my project. Spring is configured like this and nothing more:
#Configuration
#ComponentScan(value = { "megan.fox.is.hot", "as.is.lindsay.lohan" })
public class SpringConfiguration
{
}
I have looked in many places, something I found was fetching a property from PropertyPlaceholderConfigurer and stuff like that, but I didn't understand how it works and mainly it looks way too complex for what i need: just sharing one variable.
There must be an easy solution like in the JSF world, but i suspect i am looking for the wrong name in the Spring world! :)
Any help is greatly appreciated, this is the last thing I need to fix in my project!
In my XPages application, I use a managed Java bean (scope = application) for translating strings:
public class Translator extends HashMap<String,String> implements Serializable {
private static final long serialVersionUID = 1L;
public String language = "en";
public Translator() { super(); this.init(null); }
public Translator(String language) { super(); this.init(language); }
public boolean init(String language) {
try {
FacesContext context = FacesContext.getCurrentInstance();
if (language!=null) this.language=language;
Properties data = new Properties();
// load translation strings from properties file in WEB-INF
data.load(new InputStreamReader(context.getExternalContext().getResourceAsStream("WEB-INF/translations_"+this.language+".properties"),"UTF-8"));
super.putAll(new HashMap<String,String>((Map) data));
// serializing the bean to a file on disk > this part of the code is just here to easily test how often the bean is initialized
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("C:\\dump\\Translator_"+this.language+"_"+new Date().getTime()+".ser"));
out.writeObject(this);
out.close();
return true;
}
catch (Exception e) { return false; }
}
public String getLanguage() { return this.language; }
public boolean setLanguage(String language) { return this.init(language); }
// special get function which is more tolerant than HashMap.get
public String get(Object key) {
String s = (String) key;
if (super.containsKey(s)) return super.get(s);
if (super.containsKey(s.toLowerCase())) return super.get(s.toLowerCase());
String s1 = s.substring(0,1);
if (s1.toLowerCase().equals(s1)) {
s1=super.get(s1.toUpperCase()+s.substring(1));
if (s1!=null) return s1.substring(0,1).toLowerCase()+s1.substring(1);
} else {
s1=super.get(s1.toLowerCase()+s.substring(1));
if (s1!=null) return s1.substring(0,1).toUpperCase()+s1.substring(1);
}
return s;
}
}
I use "extends HashMap" because in this way i only have to write "${myTranslatorBean['someText']}" (expression language) to get the translations into my XPage. The problem is that the bean is re-initialized at EVERY complete refresh or page reload. I tested this by serializing the bean to a unique file on the disk at the end of every initialisiation. In my other managed Java beans (which do not use "extends HashMap") this problem does not occur. Can anybody tell me what's wrong with my code? Thanks in advance.
EDIT: The entry for the managed Java bean in the faces-config.xml looks like this:
<managed-bean>
<managed-bean-name>myTranslatorBean</managed-bean-name>
<managed-bean-class>com.ic.Translator</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
I concur with David about the faces-config entry - if you could post it, that could shine some light on it.
In its absence, I'll take a stab at it: are you using a managed property to set the "language" value for the app. If you are, I suspect that there's a high chance that the runtime calls the setLanguage(...) method excessively. Since you call this.init(...) in that method, that would re-run that method repeatedly as well.
As a point of code style you are free to ignore, over time I (in part due to reading others' opinions) have moved away from extending collection classes directly for this kind of use. What I do instead in this situation is create an object that implements the DataObject interface and then uses a HashMap internally to store cached values. That's part of a larger industry preference called "Composition over inheritance": http://en.wikipedia.org/wiki/Composition_over_inheritance
Just to make sure nothings weird - I suggest you post your faces-config. I use beans all the time but haven't extended HashMap in any of them. You can add a map and still use EL.
Assuming you have a map getter like "getMyMap()" then EL might be:
AppBean.myMap["myKey"]
Truth be told I don't typically use that syntax but I BELIEVE that works. I gave it a quick test and it didn't work as I expected so I'm missing something. I tried something like:
imageData.size["Large"].url
I THINK it didn't work for me because my bean doesn't IMPLEMENT Map. I notice you're EXTENDING HashMap. You might want to try implementing it. I found an interesting post here: http://blog.defrog.nl/2012/04/settings-bean-parameterized-method-call.html
Usually I do still use SSJS to pass Parameters in. It's really not the end of the would using SSJS for that. And I use EL for everything else.
This is an example of passing an object to a custom control and return a TreeSet with EL.
value="#{compositeData.imageSet.allImages}">
Regarding the bigger issue of the bean re-initializing.. That is odd.. I don't do a ton with ApplicationScope. But I suggest you play with the constructor. I'm not sure what you get by calling super() there. I would suggest use a boolean to only run any init code of the boolean wasn't already set. Obviously you then set it in the init code. See what that does.
Should it throw an exception, pass resolution to its parent or something else?
My favorite container Autofac raises an exception - which is the only thing I don't like about it.
I think that it should pass resolution to its parent which should resolve my issue with this code
class LazyClass
{
public void DoSomething() { }
}
class SomeClass
{
public event EventHandler WatchOut = (s, ea) => { };
public void Start()
{
WatchOut(this, EventArgs.Empty);
}
}
class LazyInterceptor
{
Lazy<LazyClass> lazy;
public LazyInterceptor(Lazy<LazyClass> lazy)
{
this.lazy = lazy;
}
public void Register(SomeClass some)
{
some.WatchOut += (s, ea) => lazy.Value.DoSomething();
}
}
[TestMethod]
public void LazyAndEvents()
{
var builder = new ContainerBuilder();
builder.RegisterType<LazyClass>().SingleInstance();
var container = builder.Build();
var someClass = new SomeClass();
using (var inner = container.BeginLifetimeScope(cb =>
cb.RegisterType().SingleInstance()))
{
var interceptor = inner.Resolve();
interceptor.Register(someClass);
}
someClass.Start();
}
I know of three workarounds around this, but they all seem just wrong
not disposing container - this is what I currently do, but that's just to make Autofac happy
explicitly registering lazy - seems just wrong and I don't even know what will be resolved
taking ownership for dispose - it doesn't seem as bad, but I don't use object in that way, so again it looks like a workaround. Also, in Autofac it introduces dependency which is not nice
The whole point of lifetime scopes is that to control the lifetime of the contained instances. Building on the Disposable pattern, disposing the container is a clean and well-established way of releasing the resources governed by that container. Thus, when a lifetime scope is disposed you should no longer depend on instances resolved from that lifetime scope.
So I would say that you are definitively using Autofac the wrong way here. Perhaps if you explained why you want to use the scope like that, we could figure out the proper usage.
Writing a simple JSF application I've some across the following Problem:
My entities.controller.EntityNameManager class contains a method getEntityNameSelectList() which I can use to populate a ComboBox with. This works and shows all Entities, since the Method to retrieve the Entities does not have a where clause.
This Method was automatically created.
Now I want to have a second similar Method, that filters the options based on a variable in the sessionscope. To do this I copied the original Method, renamed it to getEntityNameSelectListByUser(User theUser) and changed the Method that queries the database to one that does indeed filter by UserId.
However, when trying to load the page in the browser, I get an error stating that the controller class does not have a "EntityNameSelectListByUser" property. I assume that since my new method expects a parameter it can't be found. Is there a way I can make it aware of the Parameter or the Sessionscope userid?
Support for parameters in EL is slated for the next maintenance release of JSR 245 (announcement here; implementation here).
Assuming you don't want to wait for JEE6, you have several ways to overcome this limitation. These approached are defined in terms of POJO managed beans, so adapt them to your EJBs as appropriate.
1.
Do the session lookup and function call in a backing bean:
public String getFoo() {
FacesContext context = FacesContext
.getCurrentInstance();
ExternalContext ext = context.getExternalContext();
String bar = (String) ext.getSessionMap().get("bar");
return getFoo(bar);
}
Example binding:
#{paramBean.foo}
2.
Use an EL function (defined in a TLD, mapped to a public static method):
public static String getFoo(ParamBean bean, String bar) {
return bean.getFoo(bar);
}
Example binding:
#{baz:getFoo(paramBean, bar)}
3.
Subvert the Map class to call the function (a bit of a hack and limited to one parameter):
public Map<String, String> getFooMap() {
return new HashMap<String, String>() {
#Override
public String get(Object key) {
return getFoo((String) key);
}
};
}
Example binding:
#{paramBean.fooMap[bar]}