001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry.pageload;
016    
017    import org.apache.hivemind.ClassResolver;
018    import org.apache.tapestry.IEngine;
019    import org.apache.tapestry.IPage;
020    import org.apache.tapestry.IRequestCycle;
021    import org.apache.tapestry.Tapestry;
022    import org.apache.tapestry.engine.IMonitor;
023    import org.apache.tapestry.engine.IPageLoader;
024    import org.apache.tapestry.engine.IPageSource;
025    import org.apache.tapestry.resolver.PageSpecificationResolver;
026    import org.apache.tapestry.services.ObjectPool;
027    import org.apache.tapestry.util.MultiKey;
028    
029    /**
030     * A source for pages for a particular application. Each application should have its own
031     * <code>PageSource</code>, storing it into the {@link javax.servlet.ServletContext}using a
032     * unique key (usually built from the application name).
033     * <p>
034     * The <code>PageSource</code> acts as a pool for {@link IPage}instances. Pages are retrieved
035     * from the pool using {@link #getPage(IRequestCycle, String, IMonitor)}and are later returned to
036     * the pool using {@link #releasePage(IPage)}.
037     * <p>
038     * TBD: Pooled pages stay forever. Need a strategy for cleaning up the pool, tracking which pages
039     * have been in the pool the longest, etc.
040     * 
041     * @author Howard Lewis Ship
042     */
043    
044    public class PageSource implements IPageSource
045    {
046        /** set by container */
047        private ClassResolver _classResolver;
048    
049        /** @since 4.0 */
050        private PageSpecificationResolver _pageSpecificationResolver;
051    
052        /** @since 4.0 */
053    
054        private IPageLoader _loader;
055    
056        /**
057         * The pool of {@link IPage}s. The key is a {@link MultiKey}, built from the page name and the
058         * page locale. This is a reference to a shared pool.
059         */
060    
061        private ObjectPool _pool;
062    
063        public ClassResolver getClassResolver()
064        {
065            return _classResolver;
066        }
067    
068        /**
069         * Builds a key for a named page in the application's current locale.
070         */
071    
072        protected MultiKey buildKey(IEngine engine, String pageName)
073        {
074            Object[] keys;
075    
076            keys = new Object[]
077            { pageName, engine.getLocale() };
078    
079            // Don't make a copy, this array is just for the MultiKey.
080    
081            return new MultiKey(keys, false);
082        }
083    
084        /**
085         * Builds a key from an existing page, using the page's name and locale. This is used when
086         * storing a page into the pool.
087         */
088    
089        protected MultiKey buildKey(IPage page)
090        {
091            Object[] keys;
092    
093            keys = new Object[]
094            { page.getPageName(), page.getLocale() };
095    
096            // Don't make a copy, this array is just for the MultiKey.
097    
098            return new MultiKey(keys, false);
099        }
100    
101        /**
102         * Gets the page from a pool, or otherwise loads the page. This operation is threadsafe.
103         */
104    
105        public IPage getPage(IRequestCycle cycle, String pageName, IMonitor monitor)
106        {
107            IEngine engine = cycle.getEngine();
108            Object key = buildKey(engine, pageName);
109            IPage result = (IPage) _pool.get(key);
110    
111            if (result == null)
112            {
113                monitor.pageCreateBegin(pageName);
114    
115                _pageSpecificationResolver.resolve(cycle, pageName);
116    
117                // The loader is responsible for invoking attach(),
118                // and for firing events to PageAttachListeners
119    
120                result = _loader.loadPage(
121                        _pageSpecificationResolver.getSimplePageName(),
122                        _pageSpecificationResolver.getNamespace(),
123                        cycle,
124                        _pageSpecificationResolver.getSpecification());
125    
126                monitor.pageCreateEnd(pageName);
127            }
128            else
129            {
130                // But for pooled pages, we are responsible.
131                // This call will also fire events to any PageAttachListeners
132    
133                result.attach(engine, cycle);
134            }
135    
136            return result;
137        }
138    
139        /**
140         * Returns the page to the appropriate pool. Invokes {@link IPage#detach()}.
141         */
142    
143        public void releasePage(IPage page)
144        {
145            Tapestry.clearMethodInvocations();
146    
147            page.detach();
148    
149            Tapestry.checkMethodInvocation(Tapestry.ABSTRACTPAGE_DETACH_METHOD_ID, "detach()", page);
150    
151            _pool.store(buildKey(page), page);
152        }
153    
154        /** @since 4.0 */
155    
156        public void setPool(ObjectPool pool)
157        {
158            _pool = pool;
159        }
160    
161        /** @since 4.0 */
162    
163        public void setClassResolver(ClassResolver resolver)
164        {
165            _classResolver = resolver;
166        }
167    
168        /** @since 4.0 */
169    
170        public void setPageSpecificationResolver(PageSpecificationResolver resolver)
171        {
172            _pageSpecificationResolver = resolver;
173        }
174    
175        /** @since 4.0 */
176    
177        public void setLoader(IPageLoader loader)
178        {
179            _loader = loader;
180        }
181    
182    }