001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.jsp;
031:
032: import com.caucho.config.ConfigContext;
033: import com.caucho.config.j2ee.InjectIntrospector;
034: import com.caucho.config.program.ConfigProgram;
035: import com.caucho.java.JavaCompiler;
036: import com.caucho.jsp.cfg.JspPropertyGroup;
037: import com.caucho.loader.Environment;
038: import com.caucho.log.Log;
039: import com.caucho.server.util.CauchoSystem;
040: import com.caucho.server.webapp.WebApp;
041: import com.caucho.util.Alarm;
042: import com.caucho.util.CacheListener;
043: import com.caucho.util.LruCache;
044: import com.caucho.vfs.MemoryPath;
045: import com.caucho.vfs.Path;
046: import com.caucho.vfs.PersistentDependency;
047: import com.caucho.webbeans.context.DependentScope;
048:
049: import javax.servlet.http.HttpServletRequest;
050: import javax.servlet.http.HttpServletResponse;
051: import javax.servlet.ServletConfig;
052: import java.io.FileNotFoundException;
053: import java.util.ArrayList;
054: import java.util.Iterator;
055: import java.util.logging.Level;
056: import java.util.logging.Logger;
057:
058: /**
059: * Parent template manager for both JspManager and XtpManager. PageManager
060: * is responsible for caching pages until the underlying files change.
061: */
062: abstract public class PageManager {
063: private final static Logger log = Log.open(PageManager.class);
064:
065: static final long ACCESS_INTERVAL = 60000L;
066:
067: protected WebApp _webApp;
068: private Path _classDir;
069: private long _updateInterval = 1000;
070: private boolean _isAdapter;
071: private boolean _omitInitLog;
072: private int _pageCacheMax = 256;
073: private LruCache<String, Entry> _cache;
074:
075: // true if the manager should detect page changes and automatically recompile
076: protected boolean _autoCompile = true;
077:
078: /**
079: * Create a new PageManager
080: *
081: * @param context the servlet webApp.
082: */
083: PageManager() {
084: }
085:
086: void initWebApp(WebApp webApp) {
087: _webApp = webApp;
088:
089: _classDir = CauchoSystem.getWorkPath();
090:
091: long interval = Environment.getDependencyCheckInterval();
092:
093: JspPropertyGroup jspPropertyGroup = _webApp.getJsp();
094:
095: if (jspPropertyGroup != null) {
096: _autoCompile = jspPropertyGroup.isAutoCompile();
097:
098: if (jspPropertyGroup.getJspMax() > 0)
099: _pageCacheMax = jspPropertyGroup.getJspMax();
100:
101: if (jspPropertyGroup.getDependencyCheckInterval() != Long.MIN_VALUE)
102: interval = jspPropertyGroup
103: .getDependencyCheckInterval();
104: }
105:
106: if (interval < 0)
107: interval = Integer.MAX_VALUE / 2;
108:
109: _updateInterval = interval;
110: }
111:
112: void setPageCacheMax(int max) {
113: _pageCacheMax = max;
114: }
115:
116: public Path getClassDir() {
117: if (_classDir != null)
118: return _classDir;
119: else {
120: Path appDir = _webApp.getAppDir();
121:
122: if (appDir instanceof MemoryPath) {
123: String workPathName = ("./" + _webApp.getURL());
124: Path path = CauchoSystem.getWorkPath().lookup(
125: workPathName);
126:
127: return path;
128: } else
129: return appDir.lookup("WEB-INF/work");
130: }
131: }
132:
133: public Path getAppDir() {
134: return _webApp.getAppDir();
135: }
136:
137: /**
138: * Returns the CauchoWebApp for the manager.
139: */
140: WebApp getWebApp() {
141: return _webApp;
142: }
143:
144: /**
145: * Compiles and returns the page at the given path and uri. The uri
146: * is needed for jsp:include, etc. in the JSP pages.
147: *
148: * @param path Path to the page.
149: * @param uri uri of the page.
150: *
151: * @return the compiled JSP (or XTP) page.
152: */
153: public Page getPage(String uri, Path path) throws Exception {
154: return getPage(uri, uri, path, null);
155: }
156:
157: /**
158: * Compiles and returns the page at the given path and uri. The uri
159: * is needed for jsp:include, etc. in the JSP pages.
160: *
161: * @param path Path to the page.
162: * @param uri uri of the page.
163: *
164: * @return the compiled JSP (or XTP) page.
165: */
166: public Page getPage(String uri, String pageURI, Path path,
167: ServletConfig config) throws Exception {
168: return getPage(uri, pageURI, path, config, null);
169: }
170:
171: /**
172: * Compiles and returns the page at the given path and uri. The uri
173: * is needed for jsp:include, etc. in the JSP pages.
174: *
175: * @param uri uri of the page.
176: * @param uri uri of the page.
177: * @param path Path to the page.
178: *
179: * @return the compiled JSP (or XTP) page.
180: */
181: public Page getPage(String uri, String pageURI, Path path,
182: ServletConfig config,
183: ArrayList<PersistentDependency> dependList)
184: throws Exception {
185: LruCache<String, Entry> cache = _cache;
186:
187: if (cache == null) {
188: initPageManager();
189:
190: synchronized (this ) {
191: if (_cache == null)
192: _cache = new LruCache<String, Entry>(_pageCacheMax);
193: cache = _cache;
194: }
195: }
196:
197: Entry entry = null;
198:
199: synchronized (cache) {
200: entry = cache.get(uri);
201:
202: if (entry == null) {
203: entry = new Entry(uri);
204: cache.put(uri, entry);
205: }
206: }
207:
208: synchronized (entry) {
209: Page page = entry.getPage();
210:
211: if (page != null && !page.cauchoIsModified())
212: return page;
213: else if (page != null && !page.isDead()) {
214: try {
215: page.destroy();
216: } catch (Exception e) {
217: log.log(Level.FINE, e.toString(), e);
218: }
219: }
220:
221: if (log.isLoggable(Level.FINE)) {
222: log.fine("Jsp[] uri:" + uri + "(cp:"
223: + getWebApp().getContextPath() + ",app:"
224: + getWebApp().getAppDir() + ") -> " + path);
225: }
226:
227: Path appDir = getWebApp().getAppDir();
228:
229: String rawClassName = pageURI;
230:
231: if (path.getPath().startsWith(appDir.getPath()))
232: rawClassName = path.getPath().substring(
233: appDir.getPath().length());
234:
235: String className = JavaCompiler.mangleName("jsp/"
236: + rawClassName);
237:
238: page = createPage(path, pageURI, className, config,
239: dependList);
240:
241: if (page == null) {
242: log.fine("Jsp[] cannot create page " + path.getURL());
243:
244: throw new FileNotFoundException(getWebApp()
245: .getContextPath()
246: + pageURI);
247: }
248:
249: if (_autoCompile == false)
250: page._caucho_setNeverModified(true);
251:
252: page._caucho_setUpdateInterval(_updateInterval);
253:
254: try {
255: ArrayList<ConfigProgram> injectList = new ArrayList<ConfigProgram>();
256: InjectIntrospector.introspectInject(injectList, page
257: .getClass());
258:
259: ConfigContext env = new ConfigContext();
260:
261: for (ConfigProgram inject : injectList) {
262: inject.inject(page, env);
263: }
264: } catch (RuntimeException e) {
265: throw e;
266: } catch (Exception e) {
267: throw new RuntimeException(e);
268: }
269:
270: entry.setPage(page);
271:
272: return page;
273: }
274: }
275:
276: protected void initPageManager() {
277: }
278:
279: /**
280: * Implementation-specific method to create the actual page. JspManager
281: * and XtpManager define this for their specific needs.
282: */
283: abstract Page createPage(Path path, String uri, String className,
284: ServletConfig config,
285: ArrayList<PersistentDependency> dependList)
286: throws Exception;
287:
288: void killPage(HttpServletRequest request,
289: HttpServletResponse response, Page page) {
290: }
291:
292: /**
293: * Clean up the pages when the server shuts down.
294: */
295: void destroy() {
296: LruCache<String, Entry> cache = _cache;
297: _cache = null;
298:
299: if (cache == null)
300: return;
301:
302: synchronized (cache) {
303: Iterator<Entry> iter = cache.values();
304:
305: while (iter.hasNext()) {
306: Entry entry = iter.next();
307:
308: Page page = entry != null ? entry.getPage() : null;
309:
310: try {
311: if (page != null && !page.isDead()) {
312: page.destroy();
313: }
314: } catch (Exception e) {
315: log.log(Level.WARNING, e.toString(), e);
316: }
317: }
318: }
319: }
320:
321: class Entry implements CacheListener {
322: private String _key;
323: Page _page;
324:
325: private long _lastAccessTime;
326:
327: Entry(String key) {
328: _key = key;
329: }
330:
331: void setPage(Page page) {
332: _page = page;
333:
334: if (page != null)
335: page._caucho_setEntry(this );
336: }
337:
338: Page getPage() {
339: return _page;
340: }
341:
342: public void accessPage() {
343: long now = Alarm.getCurrentTime();
344:
345: if (now < _lastAccessTime + ACCESS_INTERVAL)
346: return;
347:
348: _lastAccessTime = now;
349:
350: if (_cache != null)
351: _cache.get(_key);
352: }
353:
354: public void removeEvent() {
355: Page page = _page;
356: _page = null;
357:
358: if (page != null && !page.isDead()) {
359: if (log.isLoggable(Level.FINE))
360: log.fine("dropping page " + page);
361:
362: page.setDead();
363: page.destroy();
364: }
365: }
366: }
367: }
|