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 > <[email protected]> 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 <[email protected]> >>> >>> >>> >>> Hi Radu! >>> >>> Yes, we already discussed this and agreed to import it. >>> Txs 4 your report! >>> >>> LieGrue, >>> strub >>> >>> >>> >>> >>> >>>> ________________________________ >>>> From: Radu Creanga <[email protected]> >>>> To: [email protected] >>>> 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) <[email protected]> >>>> 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 >>>> >>>> >>>> >> >>
