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                result = _loader.loadPage(
118                        _pageSpecificationResolver.getSimplePageName(),
119                        _pageSpecificationResolver.getNamespace(),
120                        cycle,
121                        _pageSpecificationResolver.getSpecification());
122    
123                monitor.pageCreateEnd(pageName);
124            }
125    
126            return result;
127        }
128    
129        /**
130         * Returns the page to the appropriate pool. Invokes {@link IPage#detach()}.
131         */
132    
133        public void releasePage(IPage page)
134        {
135            Tapestry.clearMethodInvocations();
136    
137            page.detach();
138    
139            Tapestry.checkMethodInvocation(Tapestry.ABSTRACTPAGE_DETACH_METHOD_ID, "detach()", page);
140    
141            _pool.store(buildKey(page), page);
142        }
143    
144        /** @since 4.0 */
145    
146        public void setPool(ObjectPool pool)
147        {
148            _pool = pool;
149        }
150    
151        /** @since 4.0 */
152    
153        public void setClassResolver(ClassResolver resolver)
154        {
155            _classResolver = resolver;
156        }
157    
158        /** @since 4.0 */
159    
160        public void setPageSpecificationResolver(PageSpecificationResolver resolver)
161        {
162            _pageSpecificationResolver = resolver;
163        }
164    
165        /** @since 4.0 */
166    
167        public void setLoader(IPageLoader loader)
168        {
169            _loader = loader;
170        }
171    
172    }