Source code: com/sun/facelets/impl/DefaultFaceletFactory.java
1 /**
2 * Licensed under the Common Development and Distribution License,
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://www.sun.com/cddl/
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
11 * implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15 package com.sun.facelets.impl;
16
17 import java.io.FileNotFoundException;
18 import java.io.IOException;
19 import java.net.URL;
20 import java.util.HashMap;
21 import java.util.Map;
22 import java.util.logging.Level;
23 import java.util.logging.Logger;
24
25 import javax.el.ELException;
26 import javax.faces.FacesException;
27 import javax.faces.context.FacesContext;
28
29 import com.sun.facelets.Facelet;
30 import com.sun.facelets.FaceletException;
31 import com.sun.facelets.FaceletFactory;
32 import com.sun.facelets.FaceletHandler;
33 import com.sun.facelets.compiler.Compiler;
34 import com.sun.facelets.util.ParameterCheck;
35 import com.sun.facelets.util.Resource;
36
37 /**
38 * Default FaceletFactory implementation.
39 *
40 * @author Jacob Hookom
41 * @version $Id: DefaultFaceletFactory.java,v 1.8 2006/05/03 04:30:13 jhook Exp $
42 */
43 public final class DefaultFaceletFactory extends FaceletFactory {
44
45 protected final static Logger log = Logger.getLogger("facelets.factory");
46
47 private final Compiler compiler;
48
49 private final Map facelets;
50
51 private final Map relativeLocations;
52
53 private final ResourceResolver resolver;
54
55 private final URL baseUrl;
56
57 private final long refreshPeriod;
58
59 public DefaultFaceletFactory(Compiler compiler, ResourceResolver resolver) throws IOException {
60 this(compiler, resolver, -1);
61 }
62
63 public DefaultFaceletFactory(Compiler compiler, ResourceResolver resolver, long refreshPeriod) {
64 ParameterCheck.notNull("compiler", compiler);
65 ParameterCheck.notNull("resolver", resolver);
66 this.compiler = compiler;
67 this.facelets = new HashMap();
68 this.relativeLocations = new HashMap();
69 this.resolver = resolver;
70 this.baseUrl = resolver.resolveUrl("/");
71 //this.location = url;
72 log.fine("Using ResourceResolver: " + resolver);
73 this.refreshPeriod = (refreshPeriod > 0) ? refreshPeriod * 1000 : -1;
74 log.fine("Using Refresh Period: " + this.refreshPeriod);
75 }
76
77 /*
78 * (non-Javadoc)
79 *
80 * @see com.sun.facelets.FaceletFactory#getFacelet(java.lang.String)
81 */
82 public Facelet getFacelet(String uri) throws IOException, FaceletException,
83 FacesException, ELException {
84 URL url = (URL) this.relativeLocations.get(uri);
85 if (url == null) {
86 url = this.resolveURL(this.baseUrl, uri);
87 if (url != null) {
88 this.relativeLocations.put(uri, url);
89 } else {
90 throw new IOException("'" + uri + "' not found.");
91 }
92 }
93 return this.getFacelet(url);
94 }
95
96 /**
97 * Resolves a path based on the passed URL. If the path starts with '/',
98 * then resolve the path against
99 * {@link javax.faces.context.ExternalContext#getResource(java.lang.String) javax.faces.context.ExternalContext#getResource(java.lang.String)}.
100 * Otherwise create a new URL via
101 * {@link URL#URL(java.net.URL, java.lang.String) URL(URL, String)}.
102 *
103 * @param source
104 * base to resolve from
105 * @param path
106 * relative path to the source
107 * @return resolved URL
108 * @throws IOException
109 */
110 public URL resolveURL(URL source, String path) throws IOException {
111 if (path.startsWith("/")) {
112 URL url = this.resolver.resolveUrl(path);
113 if (url == null) {
114 throw new FileNotFoundException(path
115 + " Not Found in ExternalContext as a Resource");
116 }
117 return url;
118 } else {
119 return new URL(source, path);
120 }
121 }
122
123 /**
124 * Create a Facelet from the passed URL. This method checks if the cached
125 * Facelet needs to be refreshed before returning. If so, uses the passed
126 * URL to build a new instance;
127 *
128 * @param url
129 * source url
130 * @return Facelet instance
131 * @throws IOException
132 * @throws FaceletException
133 * @throws FacesException
134 * @throws ELException
135 */
136 public Facelet getFacelet(URL url) throws IOException, FaceletException,
137 FacesException, ELException {
138 ParameterCheck.notNull("url", url);
139 DefaultFacelet f = (DefaultFacelet) this.facelets.get(url);
140 if (f == null || this.needsToBeRefreshed(f)) {
141 f = this.createFacelet(url);
142 this.facelets.put(url, f);
143 }
144 return f;
145 }
146
147 /**
148 * Template method for determining if the Facelet needs to be refreshed.
149 *
150 * @param facelet
151 * Facelet that could have expired
152 * @return true if it needs to be refreshed
153 */
154 protected boolean needsToBeRefreshed(DefaultFacelet facelet) {
155 if (this.refreshPeriod != -1) {
156 long ttl = facelet.getCreateTime() + this.refreshPeriod;
157 if (System.currentTimeMillis() > ttl) {
158 try {
159 long atl = facelet.getSource().openConnection()
160 .getLastModified();
161 return atl > ttl;
162 } catch (Exception e) {
163 throw new FaceletException(
164 "Error Checking Last Modified for "
165 + facelet.getAlias(), e);
166 }
167 }
168 }
169 return false;
170 }
171
172 /**
173 * Uses the internal Compiler reference to build a Facelet given the passed
174 * URL.
175 *
176 * @param url
177 * source
178 * @return a Facelet instance
179 * @throws IOException
180 * @throws FaceletException
181 * @throws FacesException
182 * @throws ELException
183 */
184 private DefaultFacelet createFacelet(URL url) throws IOException,
185 FaceletException, FacesException, ELException {
186 if (log.isLoggable(Level.FINE)) {
187 log.fine("Creating Facelet for: " + url);
188 }
189 String alias = "/"
190 + url.getFile().replaceFirst(this.baseUrl.getFile(), "");
191 try {
192 FaceletHandler h = this.compiler.compile(url, alias);
193 DefaultFacelet f = new DefaultFacelet(this, this.compiler
194 .createExpressionFactory(), url, alias, h);
195 return f;
196 } catch (FileNotFoundException fnfe) {
197 if (log.isLoggable(Level.WARNING)) {
198 log.warning(alias + " not found at " + url.toExternalForm());
199 }
200 throw new FileNotFoundException("Facelet Not Found: " + url.toExternalForm());
201 }
202 }
203
204 /**
205 * Compiler this factory uses
206 *
207 * @return final Compiler instance
208 */
209 public Compiler getCompiler() {
210 return this.compiler;
211 }
212
213 public long getRefreshPeriod() {
214 return refreshPeriod;
215 }
216 }