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 }