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 015package org.apache.tapestry.pageload; 016 017import org.apache.hivemind.ClassResolver; 018import org.apache.tapestry.IEngine; 019import org.apache.tapestry.IPage; 020import org.apache.tapestry.IRequestCycle; 021import org.apache.tapestry.Tapestry; 022import org.apache.tapestry.engine.IMonitor; 023import org.apache.tapestry.engine.IPageLoader; 024import org.apache.tapestry.engine.IPageSource; 025import org.apache.tapestry.resolver.PageSpecificationResolver; 026import org.apache.tapestry.services.ObjectPool; 027import 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 044public 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 // and for firing events to PageAttachListeners 119 120 result = _loader.loadPage( 121 _pageSpecificationResolver.getSimplePageName(), 122 _pageSpecificationResolver.getNamespace(), 123 cycle, 124 _pageSpecificationResolver.getSpecification()); 125 126 monitor.pageCreateEnd(pageName); 127 } 128 else 129 { 130 // But for pooled pages, we are responsible. 131 // This call will also fire events to any PageAttachListeners 132 133 result.attach(engine, cycle); 134 } 135 136 return result; 137 } 138 139 /** 140 * Returns the page to the appropriate pool. Invokes {@link IPage#detach()}. 141 */ 142 143 public void releasePage(IPage page) 144 { 145 Tapestry.clearMethodInvocations(); 146 147 page.detach(); 148 149 Tapestry.checkMethodInvocation(Tapestry.ABSTRACTPAGE_DETACH_METHOD_ID, "detach()", page); 150 151 _pool.store(buildKey(page), page); 152 } 153 154 /** @since 4.0 */ 155 156 public void setPool(ObjectPool pool) 157 { 158 _pool = pool; 159 } 160 161 /** @since 4.0 */ 162 163 public void setClassResolver(ClassResolver resolver) 164 { 165 _classResolver = resolver; 166 } 167 168 /** @since 4.0 */ 169 170 public void setPageSpecificationResolver(PageSpecificationResolver resolver) 171 { 172 _pageSpecificationResolver = resolver; 173 } 174 175 /** @since 4.0 */ 176 177 public void setLoader(IPageLoader loader) 178 { 179 _loader = loader; 180 } 181 182}