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 <[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 >> > >> > >> > > >
