You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltaspike.apache.org by "Radu Creanga (JIRA)" <ji...@apache.org> on 2012/11/18 22:32:58 UTC
[jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext by
observing ServletContext and HttpSession lifecycle events.
Radu Creanga created DELTASPIKE-293:
---------------------------------------
Summary: Improve the ViewScopedContext by observing ServletContext and HttpSession lifecycle events.
Key: DELTASPIKE-293
URL: https://issues.apache.org/jira/browse/DELTASPIKE-293
Project: DeltaSpike
Issue Type: Improvement
Components: JSF-Module
Affects Versions: 0.4-incubating
Reporter: Radu Creanga
Fix For: 0.5-incubating
The CDI specification states that Context implementations are responsible for destroying instances it creates. The current ViewScopedContext relies on PreDestroyViewMapEvents to be notified when a view map is destroyed. But, the JSF 2.0 and 2.1 spec only fire this event when a view map is replaced. This means that in most cases, instances created by ViewScopedContext are not properly destroyed. The ViewScopedContext should be observing ServletContext and HttpSession lifecycle events instead in order to ensure that all instances it creates are properly destroyed. Visible improvements resulting out of this would be that the @PostConstruct method of @ViewScoped beans is invoked. Additionally, this will probably result in better memory usage, since instances that are not properly destroyed are not eligible for GC.
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira
Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext by observing ServletContext and HttpSession lifecycle events.
Posted by Mark Struberg <st...@yahoo.de>.
agree. A context must not use weak references imo. If that is the case a user just uses the wrong scope ;)
As for example this e.g. looks much more like what we have (in CODI and will be in DS as well) with the @ViewAccessScoped.
But just let the ideas flow, it's always good to add fresh ideas!
LieGrue,
strub
----- Original Message -----
> From: Gerhard Petracek <ge...@gmail.com>
> To: deltaspike-dev@incubator.apache.org
> Cc:
> Sent: Tuesday, November 20, 2012 10:38 PM
> Subject: Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext by observing ServletContext and HttpSession lifecycle events.
>
> hi radu,
>
> it's way simpler - just look at the rule/s of the view-scope and not at the
> implementation you are trying to fix...
> -> imo we don't need weak references for it.
>
> once we have at least something similar to the window-handling of codi,
> it's just about some lines of code (if we agree on #2 or #3).
> (if you like #3, you can use the add-on for codi right now.)
>
> regards,
> gerhard
>
>
>
> 2012/11/20 Radu Creanga <rd...@gmail.com>
>
>> ...
>
Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext
by observing ServletContext and HttpSession lifecycle events.
Posted by Gerhard Petracek <ge...@gmail.com>.
hi radu,
it's way simpler - just look at the rule/s of the view-scope and not at the
implementation you are trying to fix...
-> imo we don't need weak references for it.
once we have at least something similar to the window-handling of codi,
it's just about some lines of code (if we agree on #2 or #3).
(if you like #3, you can use the add-on for codi right now.)
regards,
gerhard
2012/11/20 Radu Creanga <rd...@gmail.com>
> ...
Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext
by observing ServletContext and HttpSession lifecycle events.
Posted by Radu Creanga <rd...@gmail.com>.
Hello again everyone,
I apologize for the wall of text last in the last email.
Gerhard,
I looked into the implementation of WindowContext and I like the idea
of using a session scoped bean that would destroy view scoped beans in
its @PreDestroy. Boo me, I forgot I can pass the bean manager to the
context at AfterBeanDiscovery, then use it to access such a bean. So I
implemented it, see Listing 1. In this implementation I don't listen
for PreDestroyViewMapEvents, since I don't think this adds much
benefit at this point, but we might want to implement it before JSF
spec 2.2 when they're actually fired.
But, I also like the idea of weak references. Consider for example a
very long running session during which a user navigates through
numerous views, i.e. creates a lot of bean instances. With the
implementation in Listing 1, @PostConstruct in those instances won't
be called until the session ends.
If we could destroy the instances created on the first view, after it
is navigated from, but before the session is destroyed, that would be
an improvement imo. Suppose one the first view, a bean opens a
connection, but relies on the @PostConstruct to close it. If the
session lives for 30 minutes, with the implementation from Listing 1,
that connection would stay open for 30 minutes. Listing 2,
demonstrates the use of weak references to facilitate destroying view
scoped beans, on the first invocation of one of the view context
methods, right after the next GC cycle. This implementation does not
create threads.
Let me know what you guys think, I'd like to try working on a patch if
any of this sounds good.
---=== Listing 1 ===---
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Set;
import javax.enterprise.context.ContextNotActiveException;
import javax.enterprise.context.spi.Context;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
public class ViewContext implements Context {
private final BeanManager beanManager;
public ViewContext(BeanManager beanManager) {
this.beanManager = beanManager;
}
private UIViewRoot getViewRoot() {
FacesContext fc = FacesContext.getCurrentInstance();
return fc == null ? null : fc.getViewRoot();
}
@Override
public Class<? extends Annotation> getScope() {
return ViewScoped.class;
}
private ViewBeanManager getViewBeanManager() {
Set<Bean<?>> beans =
this.beanManager.getBeans(ViewBeanManager.class);
Bean<? extends Object> bean = this.beanManager.resolve(beans);
CreationalContext<? extends Object> creationalContext =
this.beanManager.createCreationalContext(bean);
return (ViewBeanManager) this.beanManager.getReference(
bean, ViewBeanManager.class, creationalContext);
}
@Override
public <T> T get(final Contextual<T> contextual,
final CreationalContext<T> creationalContext) {
UIViewRoot viewRoot = this.getViewRoot();
ViewBeanManager storage = this.getViewBeanManager();
if (viewRoot == null || storage == null) {
throw new ContextNotActiveException();
}
if (contextual instanceof Bean) {
Bean bean = (Bean) contextual;
String name = bean.getName();
Map<String, Object> viewMap = viewRoot.getViewMap();
if (viewMap.containsKey(name)) {
return (T) viewMap.get(name);
} else {
T instance = storage.create(contextual, creationalContext);
viewMap.put(name, instance);
return instance;
}
} else {
return null;
}
}
@Override
public <T> T get(Contextual<T> contextual) {
UIViewRoot viewRoot = this.getViewRoot();
if (viewRoot == null) {
throw new ContextNotActiveException();
}
if (contextual instanceof Bean) {
Bean bean = (Bean) contextual;
String name = bean.getName();
Map<String, Object> viewMap = viewRoot.getViewMap();
return (T) viewMap.get(name);
} else {
return null;
}
}
@Override
public boolean isActive() {
return this.getViewRoot() != null;
}
}
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
@SessionScoped
public class ViewBeanManager implements Serializable {
public class Info<T> implements Serializable {
private final T instance;
private final Contextual<T> contextual;
private final CreationalContext<T> creationalContext;
private Info(Contextual<T> contextual,
CreationalContext<T> creationalContext) {
this.contextual = contextual;
this.creationalContext = creationalContext;
this.instance = this.contextual.create(this.creationalContext);
}
}
private transient Set<Info> storage;
@PostConstruct
protected void postConstruct() {
this.storage = new HashSet<Info>();
}
public synchronized <T> T create(Contextual<T> contextual,
CreationalContext<T> creationalContext) {
Info<T> info = new Info<T>(
contextual, creationalContext);
this.storage.add(info );
return info .instance;
}
@PreDestroy
protected void preDestroy() {
for (Info info : this.storage) {
info .contextual.destroy(
info .instance, info .creationalContext);
}
}
}
---===Listing 2 ===---
import java.io.Serializable;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
import javax.faces.component.UIViewRoot;
@SessionScoped
public class ViewReferenceManager implements Serializable {
// identifier for the tracker object
private static final String TRACKER =
ViewReferenceManager.class.getName() + '.' + "TRACKER";
// info is now a weak reference
private class Info<T> extends WeakReference<Object> implements
Serializable {
private final T instance;
private final Contextual<T> contextual;
private final CreationalContext<T> creationalContext;
private Info(Object tracker,
ReferenceQueue<Object> queue,
Contextual<T> contextual,
CreationalContext<T> creationalContext) {
super(tracker, queue);
this.contextual = contextual;
this.creationalContext = creationalContext;
this.instance = this.contextual.create(this.creationalContext);
}
}
private transient ReferenceQueue<Object> queue;
private transient Set<Info> storage;
@PostConstruct
protected void postConstruct() {
this.queue = new ReferenceQueue<Object>();
this.storage = new HashSet<Info>();
}
// called during every get() of the context object
protected synchronized void removeStaleReferences() {
Info info = (Info) this.queue.poll();
while (info != null) {
this.storage.remove(info);
info.contextual.destroy(info.instance, info.creationalContext);
info = (Info) this.queue.poll();
}
}
protected synchronized <T> T create(UIViewRoot viewRoot,
Contextual<T> contextual, CreationalContext<T> creationalContext) {
// get or create marker object
Object tracker;
Map<String, Object> attributes = viewRoot.getAttributes();
if (attributes.containsKey(ViewReferenceManager.TRACKER)) {
tracker = attributes.get(ViewReferenceManager.TRACKER);
} else {
tracker = System.currentTimeMillis();
attributes.put(ViewReferenceManager.TRACKER, tracker);
}
Info<T> info = new Info<T>(tracker, this.queue, contextual,
creationalContext);
this.storage.add(info);
return info.instance;
}
@PreDestroy
protected void preDestroy() {
for (Info info : this.storage) {
info.contextual.destroy(
info.instance, info.creationalContext);
}
}
}
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Set;
import javax.enterprise.context.ContextNotActiveException;
import javax.enterprise.context.spi.Context;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
public class ViewContext implements Context {
private final BeanManager beanManager;
public ViewContext(BeanManager beanManager) {
this.beanManager = beanManager;
}
private UIViewRoot getViewRoot() {
FacesContext fc = FacesContext.getCurrentInstance();
return fc == null ? null : fc.getViewRoot();
}
@Override
public Class<? extends Annotation> getScope() {
return ViewScoped.class;
}
private ViewReferenceManager getReferenceManager() {
Set<Bean<?>> beans =
this.beanManager.getBeans(ViewReferenceManager.class);
Bean<? extends Object> bean = this.beanManager.resolve(beans);
CreationalContext<? extends Object> creationalContext =
this.beanManager.createCreationalContext(bean);
return (ViewReferenceManager) this.beanManager.getReference(
bean, ViewReferenceManager.class, creationalContext);
}
@Override
public <T> T get(final Contextual<T> contextual,
final CreationalContext<T> creationalContext) {
UIViewRoot viewRoot = this.getViewRoot();
ViewReferenceManager referenceManager = this.getReferenceManager();
if (viewRoot == null || referenceManager == null) {
throw new ContextNotActiveException();
} else {
referenceManager.removeStaleReferences();
}
if (contextual instanceof Bean) {
Bean bean = (Bean) contextual;
String name = bean.getName();
Map<String, Object> viewMap = viewRoot.getViewMap();
if (viewMap.containsKey(name)) {
return (T) viewMap.get(name);
} else {
T instance = referenceManager.create(
viewRoot, contextual, creationalContext);
viewMap.put(name, instance);
return instance;
}
} else {
return null;
}
}
@Override
public <T> T get(Contextual<T> contextual) {
UIViewRoot viewRoot = this.getViewRoot();
ViewReferenceManager referenceManager = this.getReferenceManager();
if (viewRoot == null || referenceManager == null) {
throw new ContextNotActiveException();
} else {
referenceManager.removeStaleReferences();
}
if (contextual instanceof Bean) {
Bean bean = (Bean) contextual;
String name = bean.getName();
Map<String, Object> viewMap = viewRoot.getViewMap();
return (T) viewMap.get(name);
} else {
return null;
}
}
@Override
public boolean isActive() {
return this.getViewRoot() != null;
}
}
Radu Creanga
On Mon, Nov 19, 2012 at 7:50 PM, Radu Creanga <rd...@gmail.com> wrote:
> Hello everyone,
>
> Pete,
>> We aren't firing servlet events in CDI 1.1 due to backwards incompatibility issues with extensions.
> Hmm, I was going off of [1]. There it stills says that it will be done, but ok.
>
> [1] http://jcp.org/en/jsr/detail?id=346
>
> Mark,
>> Yes, we already discussed this and agreed to import it.
> Sounds good.
>> Txs 4 your report!
> Happy to help, this is fun.
>
> Gerhard,
>> #1
> This should work.
>
>> #2
> I need to familiarize myself better with view-access-scope.
>
>> #3
> Also with window-scoped.
>
> Intuitively all should work just fine, which one we'll pick will prob
> depend on ease / reliability.
>
> I've been working on this since last night for our internal project
> since I can't move on without a reliable implementation, so I'd like
> to add the idea to the list.
>
> #4 I thought it would be a good idea to make a BeanInfo object
> extending WeakReference that would store the bean instance, the
> Contextual object, and the CreationalContext object with strong
> references, but store a weak reference to the view map itself and
> place the bean instance (not the BeanInfo) in the view map. Also, have
> the Context keep a reference queue and use a clean up thread that
> blocks on the queue until some weak reference has been cleared -
> which, won't happen until the view map would be up for GC. I thought
> that, even though JSF doesn't fire the PreDestroyViewMapEvent, it
> would at least release the map for GC, but it appears that this is not
> the case (the implementation reuses them?). Anyhow, I store a marker
> object in the view attributes map and link a weak reference to that
> object while still placing all the bean instances in the view map.
> Once the view goes out of scope, all the associated bean instances get
> properly destroyed right after the next CG cycle. This works for my
> needs for now as I don't need them to be destroyed right away although
> it is important that they get disposed properly eventually. It may not
> be a good general purpose implementation since depending on the server
> load, the next GC cycle may not come for quite some time. Also, I
> don't know how this play out with serialization, I'm guessing not so
> well however.
>
> Here's a rough copy / paste from my implementation:
>
> package com.concensus.athena.framework.cdi.extension;
>
> import java.lang.annotation.Annotation;
> import java.lang.ref.ReferenceQueue;
> import java.lang.ref.WeakReference;
> import java.util.HashSet;
> import java.util.Map;
> import java.util.Set;
> import javax.enterprise.context.ContextNotActiveException;
> import javax.enterprise.context.spi.Context;
> import javax.enterprise.context.spi.Contextual;
> import javax.enterprise.context.spi.CreationalContext;
> import javax.enterprise.inject.spi.Bean;
> import javax.faces.component.UIViewRoot;
> import javax.faces.context.FacesContext;
>
> public class ViewContext implements Context {
>
> private class BeanInfo<T> extends WeakReference<Object> {
>
> private final T instance;
> private final Contextual<T> contextual;
> private final CreationalContext<T> creationalContext;
>
> public BeanInfo(Object pin,
> ReferenceQueue<Object> queue,
> Contextual<T> contextual,
> CreationalContext<T> creationalContext) {
> super(pin, queue);
> this.contextual = contextual;
> this.creationalContext = creationalContext;
> this.instance = this.contextual.create(this.creationalContext);
> }
>
> public T getInstance() {
> return this.instance;
> }
>
> public void dispose() {
> this.contextual.destroy(this.instance, this.creationalContext);
> }
>
> }
>
> private boolean run;
> // needed to make sure the weak references themselves don't become
> stale before they serve their purpose
> private final Set<BeanInfo> set;
> private final ReferenceQueue<BeanInfo> queue;
> private final Thread disposer;
>
> public ViewContext() {
> this.run = true;
> this.set = new HashSet<BeanInfo>();
> this.queue = new ReferenceQueue<BeanInfo>();
> this.disposer = new Thread(new Runnable() {
>
> @Override
> public void run() {
> BeanInfo beanInfo;
> while (run) {
> try {
> beanInfo = (BeanInfo) queue.remove();
> set.remove(beanInfo);
> beanInfo.dispose();
> } catch (InterruptedException e) {
> // log
> }
> }
> }
>
> });
> this.disposer.start();
> }
>
> private UIViewRoot getViewRoot() {
> FacesContext fc = FacesContext.getCurrentInstance();
> return fc == null ? null : fc.getViewRoot();
> }
>
> private static final String MARKER =
> ViewContext.class.getName() + '.' + "MARKER";
>
> // make sure to use one marker per view for all of it's instances
> private Object getMarker(Map<String, Object> attributes) {
> if (attributes.containsKey(ViewContext.MARKER)) {
> return attributes.get(ViewContext.MARKER);
> } else {
> Long marker = System.currentTimeMillis();
> attributes.put(ViewContext.MARKER, marker);
> return marker;
> }
> }
>
> @Override
> public Class<? extends Annotation> getScope() {
> return ViewScoped.class;
> }
>
> @Override
> public <T> T get(final Contextual<T> contextual,
> final CreationalContext<T> creationalContext) {
> UIViewRoot viewRoot = this.getViewRoot();
> if (viewRoot == null) {
> throw new ContextNotActiveException();
> }
> if (contextual instanceof Bean) {
> Bean bean = (Bean) contextual;
> String name = bean.getName();
> Map<String, Object> viewMap = viewRoot.getViewMap();
> if (viewMap.containsKey(name)) {
> return (T) viewMap.get(name);
> } else {
> Object marker = this.getMarker(viewRoot.getAttributes());
> BeanInfo<T> beanInfo = new BeanInfo(
> marker, this.queue, contextual, creationalContext);
> T instance = beanInfo.getInstance();
> viewMap.put(name, instance);
> this.set.add(beanInfo);
> return instance;
> }
> } else {
> return null;
> }
> }
>
> @Override
> public <T> T get(Contextual<T> contextual) {
> UIViewRoot viewRoot = this.getViewRoot();
> if (viewRoot == null) {
> throw new ContextNotActiveException();
> }
> if (contextual instanceof Bean) {
> Bean bean = (Bean) contextual;
> String name = bean.getName();
> Map<String, Object> viewMap = viewRoot.getViewMap();
> return (T) viewMap.get(name);
> } else {
> return null;
> }
> }
>
> @Override
> public boolean isActive() {
> return this.getViewRoot() != null;
> }
>
> // this is bad, yes, does @PreDestroy work on Context objects?
> @Override
> protected void finalize() throws Throwable {
> try {
> this.run = false;
> } finally {
> super.finalize();
> }
> }
>
> }
>
> Cheers,
> Radu Creanga
>
>
> On Mon, Nov 19, 2012 at 5:35 PM, Gerhard Petracek
> <ge...@gmail.com> wrote:
>> hi radu,
>>
>> imo we have 3 easy options:
>>
>> #1
>> we continue to rely on jsf-events and just add a session-scoped bean which
>> triggers the cleanup via its @PreDestroy callback (if the cleanup wasn't
>> done already).
>> (we also need one to cleanup the window-context.)
>>
>> #2
>> since we also need to track the view-ids for the view-access-scope, we just
>> use a similar implementation (which follows the rules of the view-scope) and
>> map it to @ViewScoped.
>>
>> #3
>> like #2 but with the help of the window-context (instead of the view-map) ->
>> similar to [1].
>>
>> regards,
>> gerhard
>>
>> [1]
>> http://os890.blogspot.co.at/2011/06/session-based-view-scope-with-codi.html
>>
>>
>>
>>
>> 2012/11/19 Mark Struberg <st...@yahoo.de>
>>>
>>>
>>>
>>> Hi Radu!
>>>
>>> Yes, we already discussed this and agreed to import it.
>>> Txs 4 your report!
>>>
>>> LieGrue,
>>> strub
>>>
>>>
>>>
>>>
>>>
>>> >________________________________
>>> > From: Radu Creanga <rd...@gmail.com>
>>> >To: deltaspike-dev@incubator.apache.org
>>> >Sent: Sunday, November 18, 2012 11:56 PM
>>> >Subject: Re: [jira] [Created] (DELTASPIKE-293) Improve the
>>> > ViewScopedContext by observing ServletContext and HttpSession lifecycle
>>> > events.
>>> >
>>> >Hello everyone,
>>> >
>>> >It seems this will require ServletContext events and Session events to
>>> >be published to the CDI event bus, which in turn requires adding the
>>> >appropriate listeners to web.xml until CDI 1.1. I know Seam Solder has
>>> >these listeners implemented. Are there plans to import it into
>>> >DeltaSpike?
>>> >
>>> >Radu Creanga
>>> >
>>> >
>>> >On Sun, Nov 18, 2012 at 4:32 PM, Radu Creanga (JIRA) <ji...@apache.org>
>>> > wrote:
>>> >> Radu Creanga created DELTASPIKE-293:
>>> >> ---------------------------------------
>>> >>
>>> >> Summary: Improve the ViewScopedContext by observing
>>> >> ServletContext and HttpSession lifecycle events.
>>> >> Key: DELTASPIKE-293
>>> >> URL:
>>> >> https://issues.apache.org/jira/browse/DELTASPIKE-293
>>> >> Project: DeltaSpike
>>> >> Issue Type: Improvement
>>> >> Components: JSF-Module
>>> >> Affects Versions: 0.4-incubating
>>> >> Reporter: Radu Creanga
>>> >> Fix For: 0.5-incubating
>>> >>
>>> >>
>>> >> The CDI specification states that Context implementations are
>>> >> responsible for destroying instances it creates. The current
>>> >> ViewScopedContext relies on PreDestroyViewMapEvents to be notified when a
>>> >> view map is destroyed. But, the JSF 2.0 and 2.1 spec only fire this event
>>> >> when a view map is replaced. This means that in most cases, instances
>>> >> created by ViewScopedContext are not properly destroyed. The
>>> >> ViewScopedContext should be observing ServletContext and HttpSession
>>> >> lifecycle events instead in order to ensure that all instances it creates
>>> >> are properly destroyed. Visible improvements resulting out of this would be
>>> >> that the @PostConstruct method of @ViewScoped beans is invoked.
>>> >> Additionally, this will probably result in better memory usage, since
>>> >> instances that are not properly destroyed are not eligible for GC.
>>> >>
>>> >> --
>>> >> This message is automatically generated by JIRA.
>>> >> If you think it was sent incorrectly, please contact your JIRA
>>> >> administrators
>>> >> For more information on JIRA, see:
>>> >> http://www.atlassian.com/software/jira
>>> >
>>> >
>>> >
>>
>>
Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext
by observing ServletContext and HttpSession lifecycle events.
Posted by Gerhard Petracek <ge...@gmail.com>.
hi radu,
as mentioned before:
> once we have at least something similar to the window-handling of codi,
it's just about some lines of code
we just have to agree on #2 or #3 before we can start with it.
regards,
gerhard
2012/11/21 Radu Creanga <rd...@gmail.com>
> Hello everyone,
>
> Gerhard, you're right, it's way simpler than that. I looked over the
> rules again like you suggested and sure I was complicating things.
>
> So, the rules:
>
> 1. A @ViewScoped bean will live as long as you're submitting the form
> to the same view again and again. In other words, as long as when the
> action method(s) returns null or even void, the bean will be there in
> the next request. Once you navigate to a different view, then the bean
> will be trashed.
>
> For this we can continue to rely on JSF events, or use a navigation
> observer like in #3.
>
> 2. The case when that event is not fired is when the session expires
> but no navigation has been performed. For example, 2 get requests to
> the same URL will result in the view for the first request to never
> get cleaned up.
>
> For this, a session bean would be enough.
>
> The implementation currently present in DS destroys instances in 1,
> but not in 2. So, assuming these are the only requirements, the
> attached patch adds support for 2 (using a session bean, no weak
> references or anything like that.).
>
> Anyhow, this may not be what you all are looking for but it is quite
> simple and working (basically #1).
>
> Gerhard, I considered both #2 and #3, and I understand how it would
> work given your article and reading documentation. But, I don't yet
> have a good grasp of the code base to implement them. I'll continue
> looking into it, maybe something good comes out.
>
> Happy Thanksgiving to all the people who celebrate it tomorrow. :)
>
> Radu Creanga
Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext
by observing ServletContext and HttpSession lifecycle events.
Posted by Radu Creanga <rd...@gmail.com>.
Hello everyone,
Gerhard, you're right, it's way simpler than that. I looked over the
rules again like you suggested and sure I was complicating things.
So, the rules:
1. A @ViewScoped bean will live as long as you're submitting the form
to the same view again and again. In other words, as long as when the
action method(s) returns null or even void, the bean will be there in
the next request. Once you navigate to a different view, then the bean
will be trashed.
For this we can continue to rely on JSF events, or use a navigation
observer like in #3.
2. The case when that event is not fired is when the session expires
but no navigation has been performed. For example, 2 get requests to
the same URL will result in the view for the first request to never
get cleaned up.
For this, a session bean would be enough.
The implementation currently present in DS destroys instances in 1,
but not in 2. So, assuming these are the only requirements, the
attached patch adds support for 2 (using a session bean, no weak
references or anything like that.).
Anyhow, this may not be what you all are looking for but it is quite
simple and working (basically #1).
Gerhard, I considered both #2 and #3, and I understand how it would
work given your article and reading documentation. But, I don't yet
have a good grasp of the code base to implement them. I'll continue
looking into it, maybe something good comes out.
Happy Thanksgiving to all the people who celebrate it tomorrow. :)
Radu Creanga
On Wed, Nov 21, 2012 at 9:22 AM, Pete Muir <pm...@redhat.com> wrote:
>
> On 20 Nov 2012, at 00:50, Radu Creanga wrote:
>
>> Hello everyone,
>>
>> Pete,
>>> We aren't firing servlet events in CDI 1.1 due to backwards incompatibility issues with extensions.
>> Hmm, I was going off of [1]. There it stills says that it will be done, but ok.
>
> This was proposed but in the end the EG decided not to include it.
>
> The JCP page reflects what is proposed, not what is actually included :-)
>
>>
>> [1] http://jcp.org/en/jsr/detail?id=346
>>
>> Mark,
>>> Yes, we already discussed this and agreed to import it.
>> Sounds good.
>>> Txs 4 your report!
>> Happy to help, this is fun.
>>
>> Gerhard,
>>> #1
>> This should work.
>>
>>> #2
>> I need to familiarize myself better with view-access-scope.
>>
>>> #3
>> Also with window-scoped.
>>
>> Intuitively all should work just fine, which one we'll pick will prob
>> depend on ease / reliability.
>>
>> I've been working on this since last night for our internal project
>> since I can't move on without a reliable implementation, so I'd like
>> to add the idea to the list.
>>
>> #4 I thought it would be a good idea to make a BeanInfo object
>> extending WeakReference that would store the bean instance, the
>> Contextual object, and the CreationalContext object with strong
>> references, but store a weak reference to the view map itself and
>> place the bean instance (not the BeanInfo) in the view map. Also, have
>> the Context keep a reference queue and use a clean up thread that
>> blocks on the queue until some weak reference has been cleared -
>> which, won't happen until the view map would be up for GC. I thought
>> that, even though JSF doesn't fire the PreDestroyViewMapEvent, it
>> would at least release the map for GC, but it appears that this is not
>> the case (the implementation reuses them?). Anyhow, I store a marker
>> object in the view attributes map and link a weak reference to that
>> object while still placing all the bean instances in the view map.
>> Once the view goes out of scope, all the associated bean instances get
>> properly destroyed right after the next CG cycle. This works for my
>> needs for now as I don't need them to be destroyed right away although
>> it is important that they get disposed properly eventually. It may not
>> be a good general purpose implementation since depending on the server
>> load, the next GC cycle may not come for quite some time. Also, I
>> don't know how this play out with serialization, I'm guessing not so
>> well however.
>>
>> Here's a rough copy / paste from my implementation:
>>
>> package com.concensus.athena.framework.cdi.extension;
>>
>> import java.lang.annotation.Annotation;
>> import java.lang.ref.ReferenceQueue;
>> import java.lang.ref.WeakReference;
>> import java.util.HashSet;
>> import java.util.Map;
>> import java.util.Set;
>> import javax.enterprise.context.ContextNotActiveException;
>> import javax.enterprise.context.spi.Context;
>> import javax.enterprise.context.spi.Contextual;
>> import javax.enterprise.context.spi.CreationalContext;
>> import javax.enterprise.inject.spi.Bean;
>> import javax.faces.component.UIViewRoot;
>> import javax.faces.context.FacesContext;
>>
>> public class ViewContext implements Context {
>>
>> private class BeanInfo<T> extends WeakReference<Object> {
>>
>> private final T instance;
>> private final Contextual<T> contextual;
>> private final CreationalContext<T> creationalContext;
>>
>> public BeanInfo(Object pin,
>> ReferenceQueue<Object> queue,
>> Contextual<T> contextual,
>> CreationalContext<T> creationalContext) {
>> super(pin, queue);
>> this.contextual = contextual;
>> this.creationalContext = creationalContext;
>> this.instance = this.contextual.create(this.creationalContext);
>> }
>>
>> public T getInstance() {
>> return this.instance;
>> }
>>
>> public void dispose() {
>> this.contextual.destroy(this.instance, this.creationalContext);
>> }
>>
>> }
>>
>> private boolean run;
>> // needed to make sure the weak references themselves don't become
>> stale before they serve their purpose
>> private final Set<BeanInfo> set;
>> private final ReferenceQueue<BeanInfo> queue;
>> private final Thread disposer;
>>
>> public ViewContext() {
>> this.run = true;
>> this.set = new HashSet<BeanInfo>();
>> this.queue = new ReferenceQueue<BeanInfo>();
>> this.disposer = new Thread(new Runnable() {
>>
>> @Override
>> public void run() {
>> BeanInfo beanInfo;
>> while (run) {
>> try {
>> beanInfo = (BeanInfo) queue.remove();
>> set.remove(beanInfo);
>> beanInfo.dispose();
>> } catch (InterruptedException e) {
>> // log
>> }
>> }
>> }
>>
>> });
>> this.disposer.start();
>> }
>>
>> private UIViewRoot getViewRoot() {
>> FacesContext fc = FacesContext.getCurrentInstance();
>> return fc == null ? null : fc.getViewRoot();
>> }
>>
>> private static final String MARKER =
>> ViewContext.class.getName() + '.' + "MARKER";
>>
>> // make sure to use one marker per view for all of it's instances
>> private Object getMarker(Map<String, Object> attributes) {
>> if (attributes.containsKey(ViewContext.MARKER)) {
>> return attributes.get(ViewContext.MARKER);
>> } else {
>> Long marker = System.currentTimeMillis();
>> attributes.put(ViewContext.MARKER, marker);
>> return marker;
>> }
>> }
>>
>> @Override
>> public Class<? extends Annotation> getScope() {
>> return ViewScoped.class;
>> }
>>
>> @Override
>> public <T> T get(final Contextual<T> contextual,
>> final CreationalContext<T> creationalContext) {
>> UIViewRoot viewRoot = this.getViewRoot();
>> if (viewRoot == null) {
>> throw new ContextNotActiveException();
>> }
>> if (contextual instanceof Bean) {
>> Bean bean = (Bean) contextual;
>> String name = bean.getName();
>> Map<String, Object> viewMap = viewRoot.getViewMap();
>> if (viewMap.containsKey(name)) {
>> return (T) viewMap.get(name);
>> } else {
>> Object marker = this.getMarker(viewRoot.getAttributes());
>> BeanInfo<T> beanInfo = new BeanInfo(
>> marker, this.queue, contextual, creationalContext);
>> T instance = beanInfo.getInstance();
>> viewMap.put(name, instance);
>> this.set.add(beanInfo);
>> return instance;
>> }
>> } else {
>> return null;
>> }
>> }
>>
>> @Override
>> public <T> T get(Contextual<T> contextual) {
>> UIViewRoot viewRoot = this.getViewRoot();
>> if (viewRoot == null) {
>> throw new ContextNotActiveException();
>> }
>> if (contextual instanceof Bean) {
>> Bean bean = (Bean) contextual;
>> String name = bean.getName();
>> Map<String, Object> viewMap = viewRoot.getViewMap();
>> return (T) viewMap.get(name);
>> } else {
>> return null;
>> }
>> }
>>
>> @Override
>> public boolean isActive() {
>> return this.getViewRoot() != null;
>> }
>>
>> // this is bad, yes, does @PreDestroy work on Context objects?
>> @Override
>> protected void finalize() throws Throwable {
>> try {
>> this.run = false;
>> } finally {
>> super.finalize();
>> }
>> }
>>
>> }
>>
>> Cheers,
>> Radu Creanga
>>
>>
>> On Mon, Nov 19, 2012 at 5:35 PM, Gerhard Petracek
>> <ge...@gmail.com> wrote:
>>> hi radu,
>>>
>>> imo we have 3 easy options:
>>>
>>> #1
>>> we continue to rely on jsf-events and just add a session-scoped bean which
>>> triggers the cleanup via its @PreDestroy callback (if the cleanup wasn't
>>> done already).
>>> (we also need one to cleanup the window-context.)
>>>
>>> #2
>>> since we also need to track the view-ids for the view-access-scope, we just
>>> use a similar implementation (which follows the rules of the view-scope) and
>>> map it to @ViewScoped.
>>>
>>> #3
>>> like #2 but with the help of the window-context (instead of the view-map) ->
>>> similar to [1].
>>>
>>> regards,
>>> gerhard
>>>
>>> [1]
>>> http://os890.blogspot.co.at/2011/06/session-based-view-scope-with-codi.html
>>>
>>>
>>>
>>>
>>> 2012/11/19 Mark Struberg <st...@yahoo.de>
>>>>
>>>>
>>>>
>>>> Hi Radu!
>>>>
>>>> Yes, we already discussed this and agreed to import it.
>>>> Txs 4 your report!
>>>>
>>>> LieGrue,
>>>> strub
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>> ________________________________
>>>>> From: Radu Creanga <rd...@gmail.com>
>>>>> To: deltaspike-dev@incubator.apache.org
>>>>> Sent: Sunday, November 18, 2012 11:56 PM
>>>>> Subject: Re: [jira] [Created] (DELTASPIKE-293) Improve the
>>>>> ViewScopedContext by observing ServletContext and HttpSession lifecycle
>>>>> events.
>>>>>
>>>>> Hello everyone,
>>>>>
>>>>> It seems this will require ServletContext events and Session events to
>>>>> be published to the CDI event bus, which in turn requires adding the
>>>>> appropriate listeners to web.xml until CDI 1.1. I know Seam Solder has
>>>>> these listeners implemented. Are there plans to import it into
>>>>> DeltaSpike?
>>>>>
>>>>> Radu Creanga
>>>>>
>>>>>
>>>>> On Sun, Nov 18, 2012 at 4:32 PM, Radu Creanga (JIRA) <ji...@apache.org>
>>>>> wrote:
>>>>>> Radu Creanga created DELTASPIKE-293:
>>>>>> ---------------------------------------
>>>>>>
>>>>>> Summary: Improve the ViewScopedContext by observing
>>>>>> ServletContext and HttpSession lifecycle events.
>>>>>> Key: DELTASPIKE-293
>>>>>> URL:
>>>>>> https://issues.apache.org/jira/browse/DELTASPIKE-293
>>>>>> Project: DeltaSpike
>>>>>> Issue Type: Improvement
>>>>>> Components: JSF-Module
>>>>>> Affects Versions: 0.4-incubating
>>>>>> Reporter: Radu Creanga
>>>>>> Fix For: 0.5-incubating
>>>>>>
>>>>>>
>>>>>> The CDI specification states that Context implementations are
>>>>>> responsible for destroying instances it creates. The current
>>>>>> ViewScopedContext relies on PreDestroyViewMapEvents to be notified when a
>>>>>> view map is destroyed. But, the JSF 2.0 and 2.1 spec only fire this event
>>>>>> when a view map is replaced. This means that in most cases, instances
>>>>>> created by ViewScopedContext are not properly destroyed. The
>>>>>> ViewScopedContext should be observing ServletContext and HttpSession
>>>>>> lifecycle events instead in order to ensure that all instances it creates
>>>>>> are properly destroyed. Visible improvements resulting out of this would be
>>>>>> that the @PostConstruct method of @ViewScoped beans is invoked.
>>>>>> Additionally, this will probably result in better memory usage, since
>>>>>> instances that are not properly destroyed are not eligible for GC.
>>>>>>
>>>>>> --
>>>>>> This message is automatically generated by JIRA.
>>>>>> If you think it was sent incorrectly, please contact your JIRA
>>>>>> administrators
>>>>>> For more information on JIRA, see:
>>>>>> http://www.atlassian.com/software/jira
>>>>>
>>>>>
>>>>>
>>>
>>>
>
Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext by observing ServletContext and HttpSession lifecycle events.
Posted by Pete Muir <pm...@redhat.com>.
On 20 Nov 2012, at 00:50, Radu Creanga wrote:
> Hello everyone,
>
> Pete,
>> We aren't firing servlet events in CDI 1.1 due to backwards incompatibility issues with extensions.
> Hmm, I was going off of [1]. There it stills says that it will be done, but ok.
This was proposed but in the end the EG decided not to include it.
The JCP page reflects what is proposed, not what is actually included :-)
>
> [1] http://jcp.org/en/jsr/detail?id=346
>
> Mark,
>> Yes, we already discussed this and agreed to import it.
> Sounds good.
>> Txs 4 your report!
> Happy to help, this is fun.
>
> Gerhard,
>> #1
> This should work.
>
>> #2
> I need to familiarize myself better with view-access-scope.
>
>> #3
> Also with window-scoped.
>
> Intuitively all should work just fine, which one we'll pick will prob
> depend on ease / reliability.
>
> I've been working on this since last night for our internal project
> since I can't move on without a reliable implementation, so I'd like
> to add the idea to the list.
>
> #4 I thought it would be a good idea to make a BeanInfo object
> extending WeakReference that would store the bean instance, the
> Contextual object, and the CreationalContext object with strong
> references, but store a weak reference to the view map itself and
> place the bean instance (not the BeanInfo) in the view map. Also, have
> the Context keep a reference queue and use a clean up thread that
> blocks on the queue until some weak reference has been cleared -
> which, won't happen until the view map would be up for GC. I thought
> that, even though JSF doesn't fire the PreDestroyViewMapEvent, it
> would at least release the map for GC, but it appears that this is not
> the case (the implementation reuses them?). Anyhow, I store a marker
> object in the view attributes map and link a weak reference to that
> object while still placing all the bean instances in the view map.
> Once the view goes out of scope, all the associated bean instances get
> properly destroyed right after the next CG cycle. This works for my
> needs for now as I don't need them to be destroyed right away although
> it is important that they get disposed properly eventually. It may not
> be a good general purpose implementation since depending on the server
> load, the next GC cycle may not come for quite some time. Also, I
> don't know how this play out with serialization, I'm guessing not so
> well however.
>
> Here's a rough copy / paste from my implementation:
>
> package com.concensus.athena.framework.cdi.extension;
>
> import java.lang.annotation.Annotation;
> import java.lang.ref.ReferenceQueue;
> import java.lang.ref.WeakReference;
> import java.util.HashSet;
> import java.util.Map;
> import java.util.Set;
> import javax.enterprise.context.ContextNotActiveException;
> import javax.enterprise.context.spi.Context;
> import javax.enterprise.context.spi.Contextual;
> import javax.enterprise.context.spi.CreationalContext;
> import javax.enterprise.inject.spi.Bean;
> import javax.faces.component.UIViewRoot;
> import javax.faces.context.FacesContext;
>
> public class ViewContext implements Context {
>
> private class BeanInfo<T> extends WeakReference<Object> {
>
> private final T instance;
> private final Contextual<T> contextual;
> private final CreationalContext<T> creationalContext;
>
> public BeanInfo(Object pin,
> ReferenceQueue<Object> queue,
> Contextual<T> contextual,
> CreationalContext<T> creationalContext) {
> super(pin, queue);
> this.contextual = contextual;
> this.creationalContext = creationalContext;
> this.instance = this.contextual.create(this.creationalContext);
> }
>
> public T getInstance() {
> return this.instance;
> }
>
> public void dispose() {
> this.contextual.destroy(this.instance, this.creationalContext);
> }
>
> }
>
> private boolean run;
> // needed to make sure the weak references themselves don't become
> stale before they serve their purpose
> private final Set<BeanInfo> set;
> private final ReferenceQueue<BeanInfo> queue;
> private final Thread disposer;
>
> public ViewContext() {
> this.run = true;
> this.set = new HashSet<BeanInfo>();
> this.queue = new ReferenceQueue<BeanInfo>();
> this.disposer = new Thread(new Runnable() {
>
> @Override
> public void run() {
> BeanInfo beanInfo;
> while (run) {
> try {
> beanInfo = (BeanInfo) queue.remove();
> set.remove(beanInfo);
> beanInfo.dispose();
> } catch (InterruptedException e) {
> // log
> }
> }
> }
>
> });
> this.disposer.start();
> }
>
> private UIViewRoot getViewRoot() {
> FacesContext fc = FacesContext.getCurrentInstance();
> return fc == null ? null : fc.getViewRoot();
> }
>
> private static final String MARKER =
> ViewContext.class.getName() + '.' + "MARKER";
>
> // make sure to use one marker per view for all of it's instances
> private Object getMarker(Map<String, Object> attributes) {
> if (attributes.containsKey(ViewContext.MARKER)) {
> return attributes.get(ViewContext.MARKER);
> } else {
> Long marker = System.currentTimeMillis();
> attributes.put(ViewContext.MARKER, marker);
> return marker;
> }
> }
>
> @Override
> public Class<? extends Annotation> getScope() {
> return ViewScoped.class;
> }
>
> @Override
> public <T> T get(final Contextual<T> contextual,
> final CreationalContext<T> creationalContext) {
> UIViewRoot viewRoot = this.getViewRoot();
> if (viewRoot == null) {
> throw new ContextNotActiveException();
> }
> if (contextual instanceof Bean) {
> Bean bean = (Bean) contextual;
> String name = bean.getName();
> Map<String, Object> viewMap = viewRoot.getViewMap();
> if (viewMap.containsKey(name)) {
> return (T) viewMap.get(name);
> } else {
> Object marker = this.getMarker(viewRoot.getAttributes());
> BeanInfo<T> beanInfo = new BeanInfo(
> marker, this.queue, contextual, creationalContext);
> T instance = beanInfo.getInstance();
> viewMap.put(name, instance);
> this.set.add(beanInfo);
> return instance;
> }
> } else {
> return null;
> }
> }
>
> @Override
> public <T> T get(Contextual<T> contextual) {
> UIViewRoot viewRoot = this.getViewRoot();
> if (viewRoot == null) {
> throw new ContextNotActiveException();
> }
> if (contextual instanceof Bean) {
> Bean bean = (Bean) contextual;
> String name = bean.getName();
> Map<String, Object> viewMap = viewRoot.getViewMap();
> return (T) viewMap.get(name);
> } else {
> return null;
> }
> }
>
> @Override
> public boolean isActive() {
> return this.getViewRoot() != null;
> }
>
> // this is bad, yes, does @PreDestroy work on Context objects?
> @Override
> protected void finalize() throws Throwable {
> try {
> this.run = false;
> } finally {
> super.finalize();
> }
> }
>
> }
>
> Cheers,
> Radu Creanga
>
>
> On Mon, Nov 19, 2012 at 5:35 PM, Gerhard Petracek
> <ge...@gmail.com> wrote:
>> hi radu,
>>
>> imo we have 3 easy options:
>>
>> #1
>> we continue to rely on jsf-events and just add a session-scoped bean which
>> triggers the cleanup via its @PreDestroy callback (if the cleanup wasn't
>> done already).
>> (we also need one to cleanup the window-context.)
>>
>> #2
>> since we also need to track the view-ids for the view-access-scope, we just
>> use a similar implementation (which follows the rules of the view-scope) and
>> map it to @ViewScoped.
>>
>> #3
>> like #2 but with the help of the window-context (instead of the view-map) ->
>> similar to [1].
>>
>> regards,
>> gerhard
>>
>> [1]
>> http://os890.blogspot.co.at/2011/06/session-based-view-scope-with-codi.html
>>
>>
>>
>>
>> 2012/11/19 Mark Struberg <st...@yahoo.de>
>>>
>>>
>>>
>>> Hi Radu!
>>>
>>> Yes, we already discussed this and agreed to import it.
>>> Txs 4 your report!
>>>
>>> LieGrue,
>>> strub
>>>
>>>
>>>
>>>
>>>
>>>> ________________________________
>>>> From: Radu Creanga <rd...@gmail.com>
>>>> To: deltaspike-dev@incubator.apache.org
>>>> Sent: Sunday, November 18, 2012 11:56 PM
>>>> Subject: Re: [jira] [Created] (DELTASPIKE-293) Improve the
>>>> ViewScopedContext by observing ServletContext and HttpSession lifecycle
>>>> events.
>>>>
>>>> Hello everyone,
>>>>
>>>> It seems this will require ServletContext events and Session events to
>>>> be published to the CDI event bus, which in turn requires adding the
>>>> appropriate listeners to web.xml until CDI 1.1. I know Seam Solder has
>>>> these listeners implemented. Are there plans to import it into
>>>> DeltaSpike?
>>>>
>>>> Radu Creanga
>>>>
>>>>
>>>> On Sun, Nov 18, 2012 at 4:32 PM, Radu Creanga (JIRA) <ji...@apache.org>
>>>> wrote:
>>>>> Radu Creanga created DELTASPIKE-293:
>>>>> ---------------------------------------
>>>>>
>>>>> Summary: Improve the ViewScopedContext by observing
>>>>> ServletContext and HttpSession lifecycle events.
>>>>> Key: DELTASPIKE-293
>>>>> URL:
>>>>> https://issues.apache.org/jira/browse/DELTASPIKE-293
>>>>> Project: DeltaSpike
>>>>> Issue Type: Improvement
>>>>> Components: JSF-Module
>>>>> Affects Versions: 0.4-incubating
>>>>> Reporter: Radu Creanga
>>>>> Fix For: 0.5-incubating
>>>>>
>>>>>
>>>>> The CDI specification states that Context implementations are
>>>>> responsible for destroying instances it creates. The current
>>>>> ViewScopedContext relies on PreDestroyViewMapEvents to be notified when a
>>>>> view map is destroyed. But, the JSF 2.0 and 2.1 spec only fire this event
>>>>> when a view map is replaced. This means that in most cases, instances
>>>>> created by ViewScopedContext are not properly destroyed. The
>>>>> ViewScopedContext should be observing ServletContext and HttpSession
>>>>> lifecycle events instead in order to ensure that all instances it creates
>>>>> are properly destroyed. Visible improvements resulting out of this would be
>>>>> that the @PostConstruct method of @ViewScoped beans is invoked.
>>>>> Additionally, this will probably result in better memory usage, since
>>>>> instances that are not properly destroyed are not eligible for GC.
>>>>>
>>>>> --
>>>>> This message is automatically generated by JIRA.
>>>>> If you think it was sent incorrectly, please contact your JIRA
>>>>> administrators
>>>>> For more information on JIRA, see:
>>>>> http://www.atlassian.com/software/jira
>>>>
>>>>
>>>>
>>
>>
Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext
by observing ServletContext and HttpSession lifecycle events.
Posted by Radu Creanga <rd...@gmail.com>.
Hello everyone,
Pete,
> We aren't firing servlet events in CDI 1.1 due to backwards incompatibility issues with extensions.
Hmm, I was going off of [1]. There it stills says that it will be done, but ok.
[1] http://jcp.org/en/jsr/detail?id=346
Mark,
> Yes, we already discussed this and agreed to import it.
Sounds good.
> Txs 4 your report!
Happy to help, this is fun.
Gerhard,
> #1
This should work.
> #2
I need to familiarize myself better with view-access-scope.
> #3
Also with window-scoped.
Intuitively all should work just fine, which one we'll pick will prob
depend on ease / reliability.
I've been working on this since last night for our internal project
since I can't move on without a reliable implementation, so I'd like
to add the idea to the list.
#4 I thought it would be a good idea to make a BeanInfo object
extending WeakReference that would store the bean instance, the
Contextual object, and the CreationalContext object with strong
references, but store a weak reference to the view map itself and
place the bean instance (not the BeanInfo) in the view map. Also, have
the Context keep a reference queue and use a clean up thread that
blocks on the queue until some weak reference has been cleared -
which, won't happen until the view map would be up for GC. I thought
that, even though JSF doesn't fire the PreDestroyViewMapEvent, it
would at least release the map for GC, but it appears that this is not
the case (the implementation reuses them?). Anyhow, I store a marker
object in the view attributes map and link a weak reference to that
object while still placing all the bean instances in the view map.
Once the view goes out of scope, all the associated bean instances get
properly destroyed right after the next CG cycle. This works for my
needs for now as I don't need them to be destroyed right away although
it is important that they get disposed properly eventually. It may not
be a good general purpose implementation since depending on the server
load, the next GC cycle may not come for quite some time. Also, I
don't know how this play out with serialization, I'm guessing not so
well however.
Here's a rough copy / paste from my implementation:
package com.concensus.athena.framework.cdi.extension;
import java.lang.annotation.Annotation;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.enterprise.context.ContextNotActiveException;
import javax.enterprise.context.spi.Context;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
public class ViewContext implements Context {
private class BeanInfo<T> extends WeakReference<Object> {
private final T instance;
private final Contextual<T> contextual;
private final CreationalContext<T> creationalContext;
public BeanInfo(Object pin,
ReferenceQueue<Object> queue,
Contextual<T> contextual,
CreationalContext<T> creationalContext) {
super(pin, queue);
this.contextual = contextual;
this.creationalContext = creationalContext;
this.instance = this.contextual.create(this.creationalContext);
}
public T getInstance() {
return this.instance;
}
public void dispose() {
this.contextual.destroy(this.instance, this.creationalContext);
}
}
private boolean run;
// needed to make sure the weak references themselves don't become
stale before they serve their purpose
private final Set<BeanInfo> set;
private final ReferenceQueue<BeanInfo> queue;
private final Thread disposer;
public ViewContext() {
this.run = true;
this.set = new HashSet<BeanInfo>();
this.queue = new ReferenceQueue<BeanInfo>();
this.disposer = new Thread(new Runnable() {
@Override
public void run() {
BeanInfo beanInfo;
while (run) {
try {
beanInfo = (BeanInfo) queue.remove();
set.remove(beanInfo);
beanInfo.dispose();
} catch (InterruptedException e) {
// log
}
}
}
});
this.disposer.start();
}
private UIViewRoot getViewRoot() {
FacesContext fc = FacesContext.getCurrentInstance();
return fc == null ? null : fc.getViewRoot();
}
private static final String MARKER =
ViewContext.class.getName() + '.' + "MARKER";
// make sure to use one marker per view for all of it's instances
private Object getMarker(Map<String, Object> attributes) {
if (attributes.containsKey(ViewContext.MARKER)) {
return attributes.get(ViewContext.MARKER);
} else {
Long marker = System.currentTimeMillis();
attributes.put(ViewContext.MARKER, marker);
return marker;
}
}
@Override
public Class<? extends Annotation> getScope() {
return ViewScoped.class;
}
@Override
public <T> T get(final Contextual<T> contextual,
final CreationalContext<T> creationalContext) {
UIViewRoot viewRoot = this.getViewRoot();
if (viewRoot == null) {
throw new ContextNotActiveException();
}
if (contextual instanceof Bean) {
Bean bean = (Bean) contextual;
String name = bean.getName();
Map<String, Object> viewMap = viewRoot.getViewMap();
if (viewMap.containsKey(name)) {
return (T) viewMap.get(name);
} else {
Object marker = this.getMarker(viewRoot.getAttributes());
BeanInfo<T> beanInfo = new BeanInfo(
marker, this.queue, contextual, creationalContext);
T instance = beanInfo.getInstance();
viewMap.put(name, instance);
this.set.add(beanInfo);
return instance;
}
} else {
return null;
}
}
@Override
public <T> T get(Contextual<T> contextual) {
UIViewRoot viewRoot = this.getViewRoot();
if (viewRoot == null) {
throw new ContextNotActiveException();
}
if (contextual instanceof Bean) {
Bean bean = (Bean) contextual;
String name = bean.getName();
Map<String, Object> viewMap = viewRoot.getViewMap();
return (T) viewMap.get(name);
} else {
return null;
}
}
@Override
public boolean isActive() {
return this.getViewRoot() != null;
}
// this is bad, yes, does @PreDestroy work on Context objects?
@Override
protected void finalize() throws Throwable {
try {
this.run = false;
} finally {
super.finalize();
}
}
}
Cheers,
Radu Creanga
On Mon, Nov 19, 2012 at 5:35 PM, Gerhard Petracek
<ge...@gmail.com> wrote:
> hi radu,
>
> imo we have 3 easy options:
>
> #1
> we continue to rely on jsf-events and just add a session-scoped bean which
> triggers the cleanup via its @PreDestroy callback (if the cleanup wasn't
> done already).
> (we also need one to cleanup the window-context.)
>
> #2
> since we also need to track the view-ids for the view-access-scope, we just
> use a similar implementation (which follows the rules of the view-scope) and
> map it to @ViewScoped.
>
> #3
> like #2 but with the help of the window-context (instead of the view-map) ->
> similar to [1].
>
> regards,
> gerhard
>
> [1]
> http://os890.blogspot.co.at/2011/06/session-based-view-scope-with-codi.html
>
>
>
>
> 2012/11/19 Mark Struberg <st...@yahoo.de>
>>
>>
>>
>> Hi Radu!
>>
>> Yes, we already discussed this and agreed to import it.
>> Txs 4 your report!
>>
>> LieGrue,
>> strub
>>
>>
>>
>>
>>
>> >________________________________
>> > From: Radu Creanga <rd...@gmail.com>
>> >To: deltaspike-dev@incubator.apache.org
>> >Sent: Sunday, November 18, 2012 11:56 PM
>> >Subject: Re: [jira] [Created] (DELTASPIKE-293) Improve the
>> > ViewScopedContext by observing ServletContext and HttpSession lifecycle
>> > events.
>> >
>> >Hello everyone,
>> >
>> >It seems this will require ServletContext events and Session events to
>> >be published to the CDI event bus, which in turn requires adding the
>> >appropriate listeners to web.xml until CDI 1.1. I know Seam Solder has
>> >these listeners implemented. Are there plans to import it into
>> >DeltaSpike?
>> >
>> >Radu Creanga
>> >
>> >
>> >On Sun, Nov 18, 2012 at 4:32 PM, Radu Creanga (JIRA) <ji...@apache.org>
>> > wrote:
>> >> Radu Creanga created DELTASPIKE-293:
>> >> ---------------------------------------
>> >>
>> >> Summary: Improve the ViewScopedContext by observing
>> >> ServletContext and HttpSession lifecycle events.
>> >> Key: DELTASPIKE-293
>> >> URL:
>> >> https://issues.apache.org/jira/browse/DELTASPIKE-293
>> >> Project: DeltaSpike
>> >> Issue Type: Improvement
>> >> Components: JSF-Module
>> >> Affects Versions: 0.4-incubating
>> >> Reporter: Radu Creanga
>> >> Fix For: 0.5-incubating
>> >>
>> >>
>> >> The CDI specification states that Context implementations are
>> >> responsible for destroying instances it creates. The current
>> >> ViewScopedContext relies on PreDestroyViewMapEvents to be notified when a
>> >> view map is destroyed. But, the JSF 2.0 and 2.1 spec only fire this event
>> >> when a view map is replaced. This means that in most cases, instances
>> >> created by ViewScopedContext are not properly destroyed. The
>> >> ViewScopedContext should be observing ServletContext and HttpSession
>> >> lifecycle events instead in order to ensure that all instances it creates
>> >> are properly destroyed. Visible improvements resulting out of this would be
>> >> that the @PostConstruct method of @ViewScoped beans is invoked.
>> >> Additionally, this will probably result in better memory usage, since
>> >> instances that are not properly destroyed are not eligible for GC.
>> >>
>> >> --
>> >> This message is automatically generated by JIRA.
>> >> If you think it was sent incorrectly, please contact your JIRA
>> >> administrators
>> >> For more information on JIRA, see:
>> >> http://www.atlassian.com/software/jira
>> >
>> >
>> >
>
>
Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext
by observing ServletContext and HttpSession lifecycle events.
Posted by Gerhard Petracek <ge...@gmail.com>.
hi radu,
imo we have 3 easy options:
#1
we continue to rely on jsf-events and just add a session-scoped bean which
triggers the cleanup via its @PreDestroy callback (if the cleanup wasn't
done already).
(we also need one to cleanup the window-context.)
#2
since we also need to track the view-ids for the view-access-scope, we just
use a similar implementation (which follows the rules of the view-scope)
and map it to @ViewScoped.
#3
like #2 but with the help of the window-context (instead of the view-map)
-> similar to [1].
regards,
gerhard
[1]
http://os890.blogspot.co.at/2011/06/session-based-view-scope-with-codi.html
2012/11/19 Mark Struberg <st...@yahoo.de>
>
>
> Hi Radu!
>
> Yes, we already discussed this and agreed to import it.
> Txs 4 your report!
>
> LieGrue,
> strub
>
>
>
>
>
> >________________________________
> > From: Radu Creanga <rd...@gmail.com>
> >To: deltaspike-dev@incubator.apache.org
> >Sent: Sunday, November 18, 2012 11:56 PM
> >Subject: Re: [jira] [Created] (DELTASPIKE-293) Improve the
> ViewScopedContext by observing ServletContext and HttpSession lifecycle
> events.
> >
> >Hello everyone,
> >
> >It seems this will require ServletContext events and Session events to
> >be published to the CDI event bus, which in turn requires adding the
> >appropriate listeners to web.xml until CDI 1.1. I know Seam Solder has
> >these listeners implemented. Are there plans to import it into
> >DeltaSpike?
> >
> >Radu Creanga
> >
> >
> >On Sun, Nov 18, 2012 at 4:32 PM, Radu Creanga (JIRA) <ji...@apache.org>
> wrote:
> >> Radu Creanga created DELTASPIKE-293:
> >> ---------------------------------------
> >>
> >> Summary: Improve the ViewScopedContext by observing
> ServletContext and HttpSession lifecycle events.
> >> Key: DELTASPIKE-293
> >> URL:
> https://issues.apache.org/jira/browse/DELTASPIKE-293
> >> Project: DeltaSpike
> >> Issue Type: Improvement
> >> Components: JSF-Module
> >> Affects Versions: 0.4-incubating
> >> Reporter: Radu Creanga
> >> Fix For: 0.5-incubating
> >>
> >>
> >> The CDI specification states that Context implementations are
> responsible for destroying instances it creates. The current
> ViewScopedContext relies on PreDestroyViewMapEvents to be notified when a
> view map is destroyed. But, the JSF 2.0 and 2.1 spec only fire this event
> when a view map is replaced. This means that in most cases, instances
> created by ViewScopedContext are not properly destroyed. The
> ViewScopedContext should be observing ServletContext and HttpSession
> lifecycle events instead in order to ensure that all instances it creates
> are properly destroyed. Visible improvements resulting out of this would be
> that the @PostConstruct method of @ViewScoped beans is invoked.
> Additionally, this will probably result in better memory usage, since
> instances that are not properly destroyed are not eligible for GC.
> >>
> >> --
> >> This message is automatically generated by JIRA.
> >> If you think it was sent incorrectly, please contact your JIRA
> administrators
> >> For more information on JIRA, see:
> http://www.atlassian.com/software/jira
> >
> >
> >
>
Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext by observing ServletContext and HttpSession lifecycle events.
Posted by Mark Struberg <st...@yahoo.de>.
Hi Radu!
Yes, we already discussed this and agreed to import it.
Txs 4 your report!
LieGrue,
strub
>________________________________
> From: Radu Creanga <rd...@gmail.com>
>To: deltaspike-dev@incubator.apache.org
>Sent: Sunday, November 18, 2012 11:56 PM
>Subject: Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext by observing ServletContext and HttpSession lifecycle events.
>
>Hello everyone,
>
>It seems this will require ServletContext events and Session events to
>be published to the CDI event bus, which in turn requires adding the
>appropriate listeners to web.xml until CDI 1.1. I know Seam Solder has
>these listeners implemented. Are there plans to import it into
>DeltaSpike?
>
>Radu Creanga
>
>
>On Sun, Nov 18, 2012 at 4:32 PM, Radu Creanga (JIRA) <ji...@apache.org> wrote:
>> Radu Creanga created DELTASPIKE-293:
>> ---------------------------------------
>>
>> Summary: Improve the ViewScopedContext by observing ServletContext and HttpSession lifecycle events.
>> Key: DELTASPIKE-293
>> URL: https://issues.apache.org/jira/browse/DELTASPIKE-293
>> Project: DeltaSpike
>> Issue Type: Improvement
>> Components: JSF-Module
>> Affects Versions: 0.4-incubating
>> Reporter: Radu Creanga
>> Fix For: 0.5-incubating
>>
>>
>> The CDI specification states that Context implementations are responsible for destroying instances it creates. The current ViewScopedContext relies on PreDestroyViewMapEvents to be notified when a view map is destroyed. But, the JSF 2.0 and 2.1 spec only fire this event when a view map is replaced. This means that in most cases, instances created by ViewScopedContext are not properly destroyed. The ViewScopedContext should be observing ServletContext and HttpSession lifecycle events instead in order to ensure that all instances it creates are properly destroyed. Visible improvements resulting out of this would be that the @PostConstruct method of @ViewScoped beans is invoked. Additionally, this will probably result in better memory usage, since instances that are not properly destroyed are not eligible for GC.
>>
>> --
>> This message is automatically generated by JIRA.
>> If you think it was sent incorrectly, please contact your JIRA administrators
>> For more information on JIRA, see: http://www.atlassian.com/software/jira
>
>
>
Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext by observing ServletContext and HttpSession lifecycle events.
Posted by Pete Muir <pm...@redhat.com>.
We aren't firing servlet events in CDI 1.1 due to backwards incompatibility issues with extensions.
On 18 Nov 2012, at 22:56, Radu Creanga wrote:
> Hello everyone,
>
> It seems this will require ServletContext events and Session events to
> be published to the CDI event bus, which in turn requires adding the
> appropriate listeners to web.xml until CDI 1.1. I know Seam Solder has
> these listeners implemented. Are there plans to import it into
> DeltaSpike?
>
> Radu Creanga
>
>
> On Sun, Nov 18, 2012 at 4:32 PM, Radu Creanga (JIRA) <ji...@apache.org> wrote:
>> Radu Creanga created DELTASPIKE-293:
>> ---------------------------------------
>>
>> Summary: Improve the ViewScopedContext by observing ServletContext and HttpSession lifecycle events.
>> Key: DELTASPIKE-293
>> URL: https://issues.apache.org/jira/browse/DELTASPIKE-293
>> Project: DeltaSpike
>> Issue Type: Improvement
>> Components: JSF-Module
>> Affects Versions: 0.4-incubating
>> Reporter: Radu Creanga
>> Fix For: 0.5-incubating
>>
>>
>> The CDI specification states that Context implementations are responsible for destroying instances it creates. The current ViewScopedContext relies on PreDestroyViewMapEvents to be notified when a view map is destroyed. But, the JSF 2.0 and 2.1 spec only fire this event when a view map is replaced. This means that in most cases, instances created by ViewScopedContext are not properly destroyed. The ViewScopedContext should be observing ServletContext and HttpSession lifecycle events instead in order to ensure that all instances it creates are properly destroyed. Visible improvements resulting out of this would be that the @PostConstruct method of @ViewScoped beans is invoked. Additionally, this will probably result in better memory usage, since instances that are not properly destroyed are not eligible for GC.
>>
>> --
>> This message is automatically generated by JIRA.
>> If you think it was sent incorrectly, please contact your JIRA administrators
>> For more information on JIRA, see: http://www.atlassian.com/software/jira
Re: [jira] [Created] (DELTASPIKE-293) Improve the ViewScopedContext
by observing ServletContext and HttpSession lifecycle events.
Posted by Radu Creanga <rd...@gmail.com>.
Hello everyone,
It seems this will require ServletContext events and Session events to
be published to the CDI event bus, which in turn requires adding the
appropriate listeners to web.xml until CDI 1.1. I know Seam Solder has
these listeners implemented. Are there plans to import it into
DeltaSpike?
Radu Creanga
On Sun, Nov 18, 2012 at 4:32 PM, Radu Creanga (JIRA) <ji...@apache.org> wrote:
> Radu Creanga created DELTASPIKE-293:
> ---------------------------------------
>
> Summary: Improve the ViewScopedContext by observing ServletContext and HttpSession lifecycle events.
> Key: DELTASPIKE-293
> URL: https://issues.apache.org/jira/browse/DELTASPIKE-293
> Project: DeltaSpike
> Issue Type: Improvement
> Components: JSF-Module
> Affects Versions: 0.4-incubating
> Reporter: Radu Creanga
> Fix For: 0.5-incubating
>
>
> The CDI specification states that Context implementations are responsible for destroying instances it creates. The current ViewScopedContext relies on PreDestroyViewMapEvents to be notified when a view map is destroyed. But, the JSF 2.0 and 2.1 spec only fire this event when a view map is replaced. This means that in most cases, instances created by ViewScopedContext are not properly destroyed. The ViewScopedContext should be observing ServletContext and HttpSession lifecycle events instead in order to ensure that all instances it creates are properly destroyed. Visible improvements resulting out of this would be that the @PostConstruct method of @ViewScoped beans is invoked. Additionally, this will probably result in better memory usage, since instances that are not properly destroyed are not eligible for GC.
>
> --
> This message is automatically generated by JIRA.
> If you think it was sent incorrectly, please contact your JIRA administrators
> For more information on JIRA, see: http://www.atlassian.com/software/jira