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. A mechanism for reporting pool statistics would be
040     * useful.
041     * 
042     * @author Howard Lewis Ship
043     */
044    
045    public class PageSource implements IPageSource
046    {
047        /** set by container */
048        private ClassResolver _classResolver;
049    
050        /** @since 4.0 */
051        private PageSpecificationResolver _pageSpecificationResolver;
052    
053        /** @since 4.0 */
054    
055        private IPageLoader _loader;
056    
057        /**
058         * The pool of {@link IPage}s. The key is a {@link MultiKey}, built from the page name and the
059         * page locale. This is a reference to a shared pool.
060         */
061    
062        private ObjectPool _pool;
063    
064        public ClassResolver getClassResolver()
065        {
066            return _classResolver;
067        }
068    
069        /**
070         * Builds a key for a named page in the application's current locale.
071         */
072    
073        protected MultiKey buildKey(IEngine engine, String pageName)
074        {
075            Object[] keys;
076    
077            keys = new Object[]
078            { pageName, engine.getLocale() };
079    
080            // Don't make a copy, this array is just for the MultiKey.
081    
082            return new MultiKey(keys, false);
083        }
084    
085        /**
086         * Builds a key from an existing page, using the page's name and locale. This is used when
087         * storing a page into the pool.
088         */
089    
090        protected MultiKey buildKey(IPage page)
091        {
092            Object[] keys;
093    
094            keys = new Object[]
095            { page.getPageName(), page.getLocale() };
096    
097            // Don't make a copy, this array is just for the MultiKey.
098    
099            return new MultiKey(keys, false);
100        }
101    
102        /**
103         * Gets the page from a pool, or otherwise loads the page. This operation is threadsafe.
104         */
105    
106        public IPage getPage(IRequestCycle cycle, String pageName, IMonitor monitor)
107        {
108            IEngine engine = cycle.getEngine();
109            Object key = buildKey(engine, pageName);
110            IPage result = (IPage) _pool.get(key);
111    
112            if (result == null)
113            {
114                monitor.pageCreateBegin(pageName);
115    
116                _pageSpecificationResolver.resolve(cycle, pageName);
117    
118                result = _loader.loadPage(
119                        _pageSpecificationResolver.getSimplePageName(),
120                        _pageSpecificationResolver.getNamespace(),
121                        cycle,
122                        _pageSpecificationResolver.getSpecification());
123    
124                monitor.pageCreateEnd(pageName);
125            }
126    
127            return result;
128        }
129    
130        /**
131         * Returns the page to the appropriate pool. Invokes {@link IPage#detach()}.
132         */
133    
134        public void releasePage(IPage page)
135        {
136            Tapestry.clearMethodInvocations();
137    
138            page.detach();
139    
140            Tapestry.checkMethodInvocation(Tapestry.ABSTRACTPAGE_DETACH_METHOD_ID, "detach()", page);
141    
142            _pool.store(buildKey(page), page);
143        }
144    
145        /** @since 4.0 */
146    
147        public void setPool(ObjectPool pool)
148        {
149            _pool = pool;
150        }
151    
152        /** @since 4.0 */
153    
154        public void setClassResolver(ClassResolver resolver)
155        {
156            _classResolver = resolver;
157        }
158    
159        /** @since 4.0 */
160    
161        public void setPageSpecificationResolver(PageSpecificationResolver resolver)
162        {
163            _pageSpecificationResolver = resolver;
164        }
165    
166        /** @since 4.0 */
167    
168        public void setLoader(IPageLoader loader)
169        {
170            _loader = loader;
171        }
172    
173    }