Source code: com/sun/facelets/impl/DefaultFacelet.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.Externalizable;
18 import java.io.IOException;
19 import java.io.ObjectInput;
20 import java.io.ObjectOutput;
21 import java.net.URL;
22 import java.text.DateFormat;
23 import java.text.SimpleDateFormat;
24 import java.util.Collection;
25 import java.util.Date;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.WeakHashMap;
30 import java.util.logging.Level;
31 import java.util.logging.Logger;
32
33 import javax.el.ELException;
34 import javax.el.ExpressionFactory;
35 import javax.faces.FacesException;
36 import javax.faces.component.UIComponent;
37 import javax.faces.context.FacesContext;
38
39 import com.sun.facelets.Facelet;
40 import com.sun.facelets.FaceletContext;
41 import com.sun.facelets.FaceletException;
42 import com.sun.facelets.FaceletHandler;
43 import com.sun.facelets.tag.jsf.ComponentSupport;
44
45 /**
46 * Default Facelet implementation.
47 *
48 * @author Jacob Hookom
49 * @version $Id: DefaultFacelet.java,v 1.9 2006/04/03 05:10:38 jhook Exp $
50 */
51 final class DefaultFacelet extends Facelet {
52
53 private final Logger log = Logger.getLogger("facelets.facelet");
54
55 private final static String APPLIED_KEY = "com.sun.facelets.APPLIED";
56
57 private final String alias;
58
59 private final ExpressionFactory elFactory;
60
61 private final DefaultFaceletFactory factory;
62
63 private final long createTime;
64
65 private final long refreshPeriod;
66
67 private final Map relativePaths;
68
69 private final FaceletHandler root;
70
71 private final URL src;
72
73 public DefaultFacelet(DefaultFaceletFactory factory, ExpressionFactory el,
74 URL src, String alias, FaceletHandler root) {
75 this.factory = factory;
76 this.elFactory = el;
77 this.src = src;
78 this.root = root;
79 this.alias = alias;
80 this.createTime = System.currentTimeMillis();
81 this.refreshPeriod = this.factory.getRefreshPeriod();
82 this.relativePaths = new WeakHashMap();
83 }
84
85 /**
86 * @see com.sun.facelets.Facelet#apply(javax.faces.context.FacesContext,
87 * javax.faces.component.UIComponent)
88 */
89 public void apply(FacesContext facesContext, UIComponent parent)
90 throws IOException, FacesException, FaceletException, ELException {
91 DefaultFaceletContext ctx = new DefaultFaceletContext(facesContext,
92 this);
93 this.refresh(parent);
94 ComponentSupport.markForDeletion(parent);
95 this.root.apply(ctx, parent);
96 ComponentSupport.finalizeForDeletion(parent);
97 this.markApplied(parent);
98 }
99
100 private final void refresh(UIComponent c) {
101 if (this.refreshPeriod > 0) {
102
103 // finally remove any children marked as deleted
104 int sz = c.getChildCount();
105 if (sz > 0) {
106 UIComponent cc = null;
107 List cl = c.getChildren();
108 ApplyToken token;
109 while (--sz >= 0) {
110 cc = (UIComponent) cl.get(sz);
111 if (!cc.isTransient()) {
112 token = (ApplyToken) cc.getAttributes().get(APPLIED_KEY);
113 if (token != null && token.time < this.createTime
114 && token.alias.equals(this.alias)) {
115 if (log.isLoggable(Level.INFO)) {
116 DateFormat df = SimpleDateFormat.getTimeInstance();
117 log.info("Facelet[" + this.alias
118 + "] was modified @ "
119 + df.format(new Date(this.createTime))
120 + ", flushing component applied @ "
121 + df.format(new Date(token.time)));
122 }
123 cl.remove(sz);
124 }
125 }
126 }
127 }
128
129 // remove any facets marked as deleted
130 if (c.getFacets().size() > 0) {
131 Collection col = c.getFacets().values();
132 UIComponent fc;
133 ApplyToken token;
134 for (Iterator itr = col.iterator(); itr.hasNext();) {
135 fc = (UIComponent) itr.next();
136 if (!fc.isTransient()) {
137 token = (ApplyToken) fc.getAttributes().get(APPLIED_KEY);
138 if (token != null && token.time < this.createTime
139 && token.alias.equals(this.alias)) {
140 if (log.isLoggable(Level.INFO)) {
141 DateFormat df = SimpleDateFormat.getTimeInstance();
142 log.info("Facelet[" + this.alias
143 + "] was modified @ "
144 + df.format(new Date(this.createTime))
145 + ", flushing component applied @ "
146 + df.format(new Date(token.time)));
147 }
148 itr.remove();
149 }
150 }
151 }
152 }
153 }
154 }
155
156 private final void markApplied(UIComponent parent) {
157 if (this.refreshPeriod > 0) {
158 Iterator itr = parent.getFacetsAndChildren();
159 UIComponent c;
160 Map attr;
161 ApplyToken token = new ApplyToken(this.alias, System
162 .currentTimeMillis()
163 + this.refreshPeriod);
164 while (itr.hasNext()) {
165 c = (UIComponent) itr.next();
166 if (!c.isTransient()) {
167 attr = c.getAttributes();
168 if (!attr.containsKey(APPLIED_KEY)) {
169 attr.put(APPLIED_KEY, token);
170 }
171 }
172 }
173 }
174 }
175
176 /**
177 * Return the alias name for error messages and logging
178 *
179 * @return alias name
180 */
181 public String getAlias() {
182 return this.alias;
183 }
184
185 /**
186 * Return this Facelet's ExpressionFactory instance
187 *
188 * @return internal ExpressionFactory instance
189 */
190 public ExpressionFactory getExpressionFactory() {
191 return this.elFactory;
192 }
193
194 /**
195 * The time when this Facelet was created, NOT the URL source code
196 *
197 * @return final timestamp of when this Facelet was created
198 */
199 public long getCreateTime() {
200 return this.createTime;
201 }
202
203 /**
204 * Delegates resolution to DefaultFaceletFactory reference. Also, caches
205 * URLs for relative paths.
206 *
207 * @param path
208 * a relative url path
209 * @return URL pointing to destination
210 * @throws IOException
211 * if there is a problem creating the URL for the path specified
212 */
213 private URL getRelativePath(String path) throws IOException {
214 URL url = (URL) this.relativePaths.get(path);
215 if (url == null) {
216 url = this.factory.resolveURL(this.src, path);
217 this.relativePaths.put(path, url);
218 }
219 return url;
220 }
221
222 /**
223 * The URL this Facelet was created from.
224 *
225 * @return the URL this Facelet was created from
226 */
227 public URL getSource() {
228 return this.src;
229 }
230
231 /**
232 * Given the passed FaceletContext, apply our child FaceletHandlers to the
233 * passed parent
234 *
235 * @see FaceletHandler#apply(FaceletContext, UIComponent)
236 * @param ctx
237 * the FaceletContext to use for applying our FaceletHandlers
238 * @param parent
239 * the parent component to apply changes to
240 * @throws IOException
241 * @throws FacesException
242 * @throws FaceletException
243 * @throws ELException
244 */
245 private void include(DefaultFaceletContext ctx, UIComponent parent)
246 throws IOException, FacesException, FaceletException, ELException {
247 this.refresh(parent);
248 this.root.apply(new DefaultFaceletContext(ctx, this), parent);
249 this.markApplied(parent);
250 }
251
252 /**
253 * Used for delegation by the DefaultFaceletContext. First pulls the URL
254 * from {@link #getRelativePath(String) getRelativePath(String)}, then
255 * calls
256 * {@link #include(FaceletContext, UIComponent, URL) include(FaceletContext, UIComponent, URL)}.
257 *
258 * @see FaceletContext#includeFacelet(UIComponent, String)
259 * @param ctx
260 * FaceletContext to pass to the included Facelet
261 * @param parent
262 * UIComponent to apply changes to
263 * @param path
264 * relative path to the desired Facelet from the FaceletContext
265 * @throws IOException
266 * @throws FacesException
267 * @throws FaceletException
268 * @throws ELException
269 */
270 public void include(DefaultFaceletContext ctx, UIComponent parent, String path)
271 throws IOException, FacesException, FaceletException, ELException {
272 URL url = this.getRelativePath(path);
273 this.include(ctx, parent, url);
274 }
275
276 /**
277 * Grabs a DefaultFacelet from referenced DefaultFaceletFacotry
278 *
279 * @see DefaultFaceletFactory#getFacelet(URL)
280 * @param ctx
281 * FaceletContext to pass to the included Facelet
282 * @param parent
283 * UIComponent to apply changes to
284 * @param url
285 * URL source to include Facelet from
286 * @throws IOException
287 * @throws FacesException
288 * @throws FaceletException
289 * @throws ELException
290 */
291 public void include(DefaultFaceletContext ctx, UIComponent parent, URL url)
292 throws IOException, FacesException, FaceletException, ELException {
293 DefaultFacelet f = (DefaultFacelet) this.factory.getFacelet(url);
294 f.include(ctx, parent);
295 }
296
297 private static class ApplyToken implements Externalizable {
298 public String alias;
299
300 public long time;
301
302 public ApplyToken() {
303 }
304
305 public ApplyToken(String alias, long time) {
306 this.alias = alias;
307 this.time = time;
308 }
309
310 public void readExternal(ObjectInput in) throws IOException,
311 ClassNotFoundException {
312 this.alias = in.readUTF();
313 this.time = in.readLong();
314 }
315
316 public void writeExternal(ObjectOutput out) throws IOException {
317 out.writeUTF(this.alias);
318 out.writeLong(this.time);
319 }
320 }
321
322 public String toString() {
323 return this.alias;
324 }
325 }