Re: Please help: retrieval and proper usage of per thread DataContext

From: Mike Kienenberger (mkienen..mail.com)
Date: Wed Nov 30 2005 - 12:02:24 EST

  • Next message: Andrus Adamchik: "Re: Please help: retrieval and proper usage of per thread DataContext"

    On 11/30/05, Andrus Adamchik <andru..bjectstyle.org> wrote:
    > This will work only if
    > (a) you use it consistently and there is no other code that sticks
    > a DataContext in a session and
    > (b) you app carries no Cayenne-related state between requests (such
    > as session DataObjects).

    I took it a step further and created a DataContextManager filter that
    runs before any other filter. That takes care of (a). For (b), I
    disallow such state, and explicitly check for it and throw an error in
    the same filter at the end of the request lifecycle.

    Here's how. The JSF stuff is only used for logging, so it can be
    removed/replaced in other environments.

    package com.xyz.servlet.filter;

    import java.io.IOException;
    import java.util.Iterator;

    import javax.faces.FactoryFinder;
    import javax.faces.component.UIViewRoot;
    import javax.faces.context.ExternalContext;
    import javax.faces.context.FacesContext;
    import javax.faces.context.FacesContextFactory;
    import javax.faces.lifecycle.Lifecycle;
    import javax.faces.lifecycle.LifecycleFactory;
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;

    import org.objectstyle.cayenne.DataObject;
    import org.objectstyle.cayenne.access.DataContext;
    import org.objectstyle.cayenne.conf.ServletUtil;

    public class DataContextManagerFilter implements Filter
    {
        public void init(FilterConfig arg0) throws ServletException
        {
        }

        public void doFilter(ServletRequest servletRequest,
    ServletResponse servletResponse, FilterChain chain) throws
    IOException, ServletException
        {
            requestInitialized(servletRequest, servletResponse);

            chain.doFilter(servletRequest, servletResponse);

            requestDestroyed(servletRequest, servletResponse);
        }

        public void destroy()
        {
        }

        public void requestInitialized(ServletRequest servletRequest,
    ServletResponse servletResponse)
        {
            HttpSession session =
    ((HttpServletRequest)servletRequest).getSession(true);
            DataContext dataContext=
    ServletUtil.getSessionContext(session);
            DataContext.bindThreadDataContext(dataContext);
        }

        public void requestDestroyed(ServletRequest servletRequest,
    ServletResponse servletResponse)
        {
            DataContext dataContext = DataContext.getThreadDataContext();

            // Try to get it first
            boolean createdFacesContext = false;
            FacesContext facesContext = FacesContext.getCurrentInstance();
            if (facesContext == null)
            {
                facesContext = createFacesContext(servletRequest, servletResponse);
                createdFacesContext = true;
            }

            try
            {
                reportUncommittedObjects(dataContext, facesContext);
            }
            finally
            {
                if (createdFacesContext)
                {
                    facesContext.release();
                }
            }

            dataContext.rollbackChanges();

            DataContext.bindThreadDataContext(null);
        }

        ///////////////////////////////////////

        // You need an inner class to be able to call
        // FacesContext.setCurrentInstance
        // since it's a protected method
        private abstract static class InnerFacesContext extends FacesContext
        {
            protected static void setFacesContextAsCurrentInstance(
                    FacesContext facesContext)
            {
                FacesContext.setCurrentInstance(facesContext);
            }
        }

        private FacesContext createFacesContext(ServletRequest request,
                ServletResponse response)
        {
            FacesContextFactory contextFactory = (FacesContextFactory) FactoryFinder
                    .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
            LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
                    .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
            Lifecycle lifecycle = lifecycleFactory
                    .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);

            // Either set a private member servletContext =
            // filterConfig.getServletContext();
            // in you filter init() method or set it here like this:
            ServletContext servletContext =
    ((HttpServletRequest)request).getSession().getServletContext();
            // Note that the above line would fail if you are using any other
            // protocol than http

            // Doesn't set this instance as the current instance of
            // FacesContext.getCurrentInstance
            FacesContext facesContext =
    contextFactory.getFacesContext(servletContext, request,
                    response, lifecycle);

            // Set using our inner class
            InnerFacesContext.setFacesContextAsCurrentInstance(facesContext);

            // set a new viewRoot, otherwise context.getViewRoot returns null
            UIViewRoot view = facesContext.getApplication().getViewHandler()
                    .createView(facesContext, "yourOwnID");
            facesContext.setViewRoot(view);

            return facesContext;
        }

        private void reportUncommittedObjects(DataContext dataContext,
    FacesContext facesContext)
        {
            if (dataContext.hasChanges())
            {
                ExternalContext externalContext = facesContext.getExternalContext();

                if (0 != dataContext.newObjects().size())
                {
                    Iterator objectIterator = dataContext.newObjects().iterator();
                    while (objectIterator.hasNext())
                    {
                        DataObject dataObject = (DataObject) objectIterator.next();
                        externalContext.log("Leftover new object: " + dataObject);
                    }
                }

                if (0 != dataContext.modifiedObjects().size())
                {
                    Iterator objectIterator =
    dataContext.modifiedObjects().iterator();
                    while (objectIterator.hasNext())
                    {
                        DataObject dataObject = (DataObject) objectIterator.next();
                        externalContext.log("Leftover modified object: " +
    dataObject);
                    }
                }

                if (0 != dataContext.deletedObjects().size())
                {
                    Iterator objectIterator =
    dataContext.deletedObjects().iterator();
                    while (objectIterator.hasNext())
                    {
                        DataObject dataObject = (DataObject) objectIterator.next();
                        externalContext.log("Leftover deleted object: " +
    dataObject);
                    }
                }

                // Maybe remove later?
                throw new RuntimeException("DataContext is dirty.");
            }
        }

    }



    This archive was generated by hypermail 2.0.0 : Wed Nov 30 2005 - 12:02:26 EST