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.commons.pool.BaseKeyedPoolableObjectFactory; 018 import org.apache.commons.pool.impl.GenericKeyedObjectPool; 019 import org.apache.hivemind.ApplicationRuntimeException; 020 import org.apache.hivemind.ClassResolver; 021 import org.apache.hivemind.events.RegistryShutdownListener; 022 import org.apache.tapestry.IEngine; 023 import org.apache.tapestry.IPage; 024 import org.apache.tapestry.IRequestCycle; 025 import org.apache.tapestry.Tapestry; 026 import org.apache.tapestry.engine.IPageLoader; 027 import org.apache.tapestry.engine.IPageSource; 028 import org.apache.tapestry.engine.IPropertySource; 029 import org.apache.tapestry.event.ReportStatusEvent; 030 import org.apache.tapestry.event.ReportStatusListener; 031 import org.apache.tapestry.event.ResetEventListener; 032 import org.apache.tapestry.internal.pageload.PageKey; 033 import org.apache.tapestry.resolver.PageSpecificationResolver; 034 035 /** 036 * A source for pages for a particular application. Each application should have its own 037 * <code>PageSource</code>, storing it into the {@link javax.servlet.ServletContext}using a 038 * unique key (usually built from the application name). 039 * <p> 040 * The <code>PageSource</code> acts as a pool for {@link IPage}instances. Pages are retrieved 041 * from the pool using {@link #getPage(IRequestCycle, String)}and are later returned to 042 * the pool using {@link #releasePage(IPage)}. 043 * <p> 044 * TBD: Pooled pages stay forever. Need a strategy for cleaning up the pool, tracking which pages 045 * have been in the pool the longest, etc. 046 * 047 * @author Howard Lewis Ship 048 */ 049 050 public class PageSource extends BaseKeyedPoolableObjectFactory implements IPageSource, ResetEventListener, ReportStatusListener, RegistryShutdownListener { 051 052 /** set by container. */ 053 private ClassResolver _classResolver; 054 055 /** @since 4.0 */ 056 private PageSpecificationResolver _pageSpecificationResolver; 057 058 /** @since 4.0 */ 059 060 private IPageLoader _loader; 061 062 private IPropertySource _propertySource; 063 064 private String _serviceId; 065 066 /** 067 * Thread safe reference to current request. 068 */ 069 private IRequestCycle _cycle; 070 071 static final long MINUTE = 1000 * 60; 072 073 /** 074 * The pool of {@link IPage}s. The key is a {@link org.apache.tapestry.util.MultiKey}, 075 * built from the page name and the page locale. 076 */ 077 GenericKeyedObjectPool _pool; 078 079 public void initializeService() 080 { 081 _pool = new GenericKeyedObjectPool(this); 082 083 _pool.setMaxActive(Integer.parseInt(_propertySource.getPropertyValue("org.apache.tapestry.page-pool-max-active"))); 084 _pool.setMaxIdle(Integer.parseInt(_propertySource.getPropertyValue("org.apache.tapestry.page-pool-max-idle"))); 085 086 _pool.setMinIdle(Integer.parseInt(_propertySource.getPropertyValue("org.apache.tapestry.page-pool-min-idle"))); 087 088 _pool.setMinEvictableIdleTimeMillis(MINUTE * Long.parseLong(_propertySource.getPropertyValue("org.apache.tapestry.page-pool-evict-idle-page-minutes"))); 089 _pool.setTimeBetweenEvictionRunsMillis(MINUTE * Long.parseLong(_propertySource.getPropertyValue("org.apache.tapestry.page-pool-evict-thread-sleep-minutes"))); 090 091 _pool.setTestWhileIdle(false); 092 _pool.setTestOnBorrow(false); 093 _pool.setTestOnReturn(false); 094 } 095 096 public void registryDidShutdown() 097 { 098 try 099 { 100 _pool.close(); 101 } catch (Exception e) { 102 // ignore 103 } 104 } 105 106 public ClassResolver getClassResolver() 107 { 108 return _classResolver; 109 } 110 111 /** 112 * Builds a key for a named page in the application's current locale. 113 * 114 * @param engine 115 * The current engine servicing this request. 116 * @param pageName 117 * The name of the page to build key for. 118 * 119 * @return The unique key for ths specified page and current {@link java.util.Locale}. 120 */ 121 122 protected PageKey buildKey(IEngine engine, String pageName) 123 { 124 return new PageKey(pageName, engine.getLocale()); 125 } 126 127 /** 128 * Builds a key from an existing page, using the page's name and locale. This is used when 129 * storing a page into the pool. 130 * 131 * @param page 132 * The page to build the key for. 133 * 134 * @return The unique key for the specified page instance. 135 */ 136 137 protected PageKey buildKey(IPage page) 138 { 139 return new PageKey(page.getPageName(), page.getLocale()); 140 } 141 142 public Object makeObject(Object key) 143 throws Exception 144 { 145 PageKey pageKey = (PageKey) key; 146 147 _pageSpecificationResolver.resolve(_cycle, pageKey.getPageName()); 148 149 // The loader is responsible for invoking attach(), 150 // and for firing events to PageAttachListeners 151 152 return _loader.loadPage(_pageSpecificationResolver.getSimplePageName(), 153 _pageSpecificationResolver.getNamespace(), 154 _cycle, 155 _pageSpecificationResolver.getSpecification()); 156 } 157 158 /** 159 * Gets the page from a pool, or otherwise loads the page. This operation is threadsafe. 160 */ 161 162 public IPage getPage(IRequestCycle cycle, String pageName) 163 { 164 165 IEngine engine = cycle.getEngine(); 166 Object key = buildKey(engine, pageName); 167 168 IPage result; 169 170 // lock our page specific key lock first 171 // This is only a temporary measure until a more robust 172 // page pool implementation can be created. 173 174 try 175 { 176 result = (IPage) _pool.borrowObject(key); 177 178 } catch (Exception ex) 179 { 180 if (RuntimeException.class.isInstance(ex)) 181 throw (RuntimeException)ex; 182 else 183 throw new ApplicationRuntimeException(PageloadMessages.errorPagePoolGet(key), ex); 184 } 185 186 187 if (result.getEngine() == null) 188 { 189 // This call will also fire events to any PageAttachListeners 190 191 result.attach(engine, cycle); 192 } 193 194 return result; 195 } 196 197 /** 198 * Returns the page to the appropriate pool. Invokes {@link IPage#detach()}. 199 */ 200 201 public void releasePage(IPage page) 202 { 203 Tapestry.clearMethodInvocations(); 204 205 page.detach(); 206 207 Tapestry.checkMethodInvocation(Tapestry.ABSTRACTPAGE_DETACH_METHOD_ID, "detach()", page); 208 209 PageKey key = buildKey(page); 210 211 try 212 { 213 _pool.returnObject(key, page); 214 215 } catch (Exception ex) 216 { 217 if (RuntimeException.class.isInstance(ex)) 218 throw (RuntimeException)ex; 219 else 220 throw new ApplicationRuntimeException(PageloadMessages.errorPagePoolGet(key), ex); 221 } 222 } 223 224 public void resetEventDidOccur() 225 { 226 _pool.clear(); 227 } 228 229 public void reportStatus(ReportStatusEvent event) 230 { 231 event.title(_serviceId); 232 233 event.section("Page Pool"); 234 235 event.property("active", _pool.getNumActive()); 236 event.property("idle", _pool.getNumIdle()); 237 } 238 239 public void setServiceId(String serviceId) 240 { 241 _serviceId = serviceId; 242 } 243 244 public void setRequestCycle(IRequestCycle cycle) 245 { 246 _cycle = cycle; 247 } 248 249 /** @since 4.0 */ 250 251 public void setClassResolver(ClassResolver resolver) 252 { 253 _classResolver = resolver; 254 } 255 256 /** @since 4.0 */ 257 258 public void setPageSpecificationResolver(PageSpecificationResolver resolver) 259 { 260 _pageSpecificationResolver = resolver; 261 } 262 263 /** @since 4.0 */ 264 265 public void setLoader(IPageLoader loader) 266 { 267 _loader = loader; 268 } 269 270 public void setPropertySource(IPropertySource propertySource) 271 { 272 _propertySource = propertySource; 273 } 274 }