001: /**
002: * Licensed under the Common Development and Distribution License,
003: * you may not use this file except in compliance with the License.
004: * You may obtain a copy of the License at
005: *
006: * http://www.sun.com/cddl/
007: *
008: * Unless required by applicable law or agreed to in writing, software
009: * distributed under the License is distributed on an "AS IS" BASIS,
010: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * permissions and limitations under the License.
013: */package com.sun.facelets.impl;
014:
015: import java.io.Externalizable;
016: import java.io.IOException;
017: import java.io.ObjectInput;
018: import java.io.ObjectOutput;
019: import java.net.URL;
020: import java.text.DateFormat;
021: import java.text.SimpleDateFormat;
022: import java.util.Collection;
023: import java.util.Date;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.WeakHashMap;
028: import java.util.logging.Level;
029: import java.util.logging.Logger;
030:
031: import javax.el.ELException;
032: import javax.el.ExpressionFactory;
033: import javax.faces.FacesException;
034: import javax.faces.component.UIComponent;
035: import javax.faces.context.FacesContext;
036:
037: import com.sun.facelets.Facelet;
038: import com.sun.facelets.FaceletContext;
039: import com.sun.facelets.FaceletException;
040: import com.sun.facelets.FaceletHandler;
041: import com.sun.facelets.tag.jsf.ComponentSupport;
042:
043: /**
044: * Default Facelet implementation.
045: *
046: * @author Jacob Hookom
047: * @version $Id: DefaultFacelet.java,v 1.9 2006/04/03 05:10:38 jhook Exp $
048: */
049: final class DefaultFacelet extends Facelet {
050:
051: private final Logger log = Logger.getLogger("facelets.facelet");
052:
053: private final static String APPLIED_KEY = "com.sun.facelets.APPLIED";
054:
055: private final String alias;
056:
057: private final ExpressionFactory elFactory;
058:
059: private final DefaultFaceletFactory factory;
060:
061: private final long createTime;
062:
063: private final long refreshPeriod;
064:
065: private final Map relativePaths;
066:
067: private final FaceletHandler root;
068:
069: private final URL src;
070:
071: public DefaultFacelet(DefaultFaceletFactory factory,
072: ExpressionFactory el, URL src, String alias,
073: FaceletHandler root) {
074: this .factory = factory;
075: this .elFactory = el;
076: this .src = src;
077: this .root = root;
078: this .alias = alias;
079: this .createTime = System.currentTimeMillis();
080: this .refreshPeriod = this .factory.getRefreshPeriod();
081: this .relativePaths = new WeakHashMap();
082: }
083:
084: /**
085: * @see com.sun.facelets.Facelet#apply(javax.faces.context.FacesContext,
086: * javax.faces.component.UIComponent)
087: */
088: public void apply(FacesContext facesContext, UIComponent parent)
089: throws IOException, FacesException, FaceletException,
090: ELException {
091: DefaultFaceletContext ctx = new DefaultFaceletContext(
092: facesContext, this );
093: this .refresh(parent);
094: ComponentSupport.markForDeletion(parent);
095: this .root.apply(ctx, parent);
096: ComponentSupport.finalizeForDeletion(parent);
097: this .markApplied(parent);
098: }
099:
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(
113: APPLIED_KEY);
114: if (token != null
115: && token.time < this .createTime
116: && token.alias.equals(this .alias)) {
117: if (log.isLoggable(Level.INFO)) {
118: DateFormat df = SimpleDateFormat
119: .getTimeInstance();
120: log
121: .info("Facelet["
122: + this .alias
123: + "] was modified @ "
124: + df
125: .format(new Date(
126: this .createTime))
127: + ", flushing component applied @ "
128: + df.format(new Date(
129: token.time)));
130: }
131: cl.remove(sz);
132: }
133: }
134: }
135: }
136:
137: // remove any facets marked as deleted
138: if (c.getFacets().size() > 0) {
139: Collection col = c.getFacets().values();
140: UIComponent fc;
141: ApplyToken token;
142: for (Iterator itr = col.iterator(); itr.hasNext();) {
143: fc = (UIComponent) itr.next();
144: if (!fc.isTransient()) {
145: token = (ApplyToken) fc.getAttributes().get(
146: APPLIED_KEY);
147: if (token != null
148: && token.time < this .createTime
149: && token.alias.equals(this .alias)) {
150: if (log.isLoggable(Level.INFO)) {
151: DateFormat df = SimpleDateFormat
152: .getTimeInstance();
153: log
154: .info("Facelet["
155: + this .alias
156: + "] was modified @ "
157: + df
158: .format(new Date(
159: this .createTime))
160: + ", flushing component applied @ "
161: + df.format(new Date(
162: token.time)));
163: }
164: itr.remove();
165: }
166: }
167: }
168: }
169: }
170: }
171:
172: private final void markApplied(UIComponent parent) {
173: if (this .refreshPeriod > 0) {
174: Iterator itr = parent.getFacetsAndChildren();
175: UIComponent c;
176: Map attr;
177: ApplyToken token = new ApplyToken(this .alias, System
178: .currentTimeMillis()
179: + this .refreshPeriod);
180: while (itr.hasNext()) {
181: c = (UIComponent) itr.next();
182: if (!c.isTransient()) {
183: attr = c.getAttributes();
184: if (!attr.containsKey(APPLIED_KEY)) {
185: attr.put(APPLIED_KEY, token);
186: }
187: }
188: }
189: }
190: }
191:
192: /**
193: * Return the alias name for error messages and logging
194: *
195: * @return alias name
196: */
197: public String getAlias() {
198: return this .alias;
199: }
200:
201: /**
202: * Return this Facelet's ExpressionFactory instance
203: *
204: * @return internal ExpressionFactory instance
205: */
206: public ExpressionFactory getExpressionFactory() {
207: return this .elFactory;
208: }
209:
210: /**
211: * The time when this Facelet was created, NOT the URL source code
212: *
213: * @return final timestamp of when this Facelet was created
214: */
215: public long getCreateTime() {
216: return this .createTime;
217: }
218:
219: /**
220: * Delegates resolution to DefaultFaceletFactory reference. Also, caches
221: * URLs for relative paths.
222: *
223: * @param path
224: * a relative url path
225: * @return URL pointing to destination
226: * @throws IOException
227: * if there is a problem creating the URL for the path specified
228: */
229: private URL getRelativePath(String path) throws IOException {
230: URL url = (URL) this .relativePaths.get(path);
231: if (url == null) {
232: url = this .factory.resolveURL(this .src, path);
233: this .relativePaths.put(path, url);
234: }
235: return url;
236: }
237:
238: /**
239: * The URL this Facelet was created from.
240: *
241: * @return the URL this Facelet was created from
242: */
243: public URL getSource() {
244: return this .src;
245: }
246:
247: /**
248: * Given the passed FaceletContext, apply our child FaceletHandlers to the
249: * passed parent
250: *
251: * @see FaceletHandler#apply(FaceletContext, UIComponent)
252: * @param ctx
253: * the FaceletContext to use for applying our FaceletHandlers
254: * @param parent
255: * the parent component to apply changes to
256: * @throws IOException
257: * @throws FacesException
258: * @throws FaceletException
259: * @throws ELException
260: */
261: private void include(DefaultFaceletContext ctx, UIComponent parent)
262: throws IOException, FacesException, FaceletException,
263: ELException {
264: this .refresh(parent);
265: this .root.apply(new DefaultFaceletContext(ctx, this ), parent);
266: this .markApplied(parent);
267: }
268:
269: /**
270: * Used for delegation by the DefaultFaceletContext. First pulls the URL
271: * from {@link #getRelativePath(String) getRelativePath(String)}, then
272: * calls
273: * {@link #include(FaceletContext, UIComponent, URL) include(FaceletContext, UIComponent, URL)}.
274: *
275: * @see FaceletContext#includeFacelet(UIComponent, String)
276: * @param ctx
277: * FaceletContext to pass to the included Facelet
278: * @param parent
279: * UIComponent to apply changes to
280: * @param path
281: * relative path to the desired Facelet from the FaceletContext
282: * @throws IOException
283: * @throws FacesException
284: * @throws FaceletException
285: * @throws ELException
286: */
287: public void include(DefaultFaceletContext ctx, UIComponent parent,
288: String path) throws IOException, FacesException,
289: FaceletException, ELException {
290: URL url = this .getRelativePath(path);
291: this .include(ctx, parent, url);
292: }
293:
294: /**
295: * Grabs a DefaultFacelet from referenced DefaultFaceletFacotry
296: *
297: * @see DefaultFaceletFactory#getFacelet(URL)
298: * @param ctx
299: * FaceletContext to pass to the included Facelet
300: * @param parent
301: * UIComponent to apply changes to
302: * @param url
303: * URL source to include Facelet from
304: * @throws IOException
305: * @throws FacesException
306: * @throws FaceletException
307: * @throws ELException
308: */
309: public void include(DefaultFaceletContext ctx, UIComponent parent,
310: URL url) throws IOException, FacesException,
311: FaceletException, ELException {
312: DefaultFacelet f = (DefaultFacelet) this .factory
313: .getFacelet(url);
314: f.include(ctx, parent);
315: }
316:
317: private static class ApplyToken implements Externalizable {
318: public String alias;
319:
320: public long time;
321:
322: public ApplyToken() {
323: }
324:
325: public ApplyToken(String alias, long time) {
326: this .alias = alias;
327: this .time = time;
328: }
329:
330: public void readExternal(ObjectInput in) throws IOException,
331: ClassNotFoundException {
332: this .alias = in.readUTF();
333: this .time = in.readLong();
334: }
335:
336: public void writeExternal(ObjectOutput out) throws IOException {
337: out.writeUTF(this .alias);
338: out.writeLong(this .time);
339: }
340: }
341:
342: public String toString() {
343: return this.alias;
344: }
345: }
|