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