2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shale.remoting;
19  
20  import java.beans.Beans;
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.ResourceBundle;
26  import javax.faces.component.UIComponent;
27  import javax.faces.context.FacesContext;
28  import javax.faces.context.ResponseWriter;
29  import org.apache.shale.remoting.faces.MappingsHelper;
30  
31  /***
32   * <p>Helper bean for rendering links to download resources commonly used
33   * in HTML and XHTML pages.  The specified resource identifier is automatically
34   * mapped based upon the Shale Remoting configuration that this application
35   * is using, as well as adapting to the servlet mapping for the JavaServer
36   * Faces controller servlet.  A given resource identifier will only be linked
37   * once for a given request.</p>
38   *
39   * <p>Instances of this class are stateless and have no side effects.</p>
40   */
41  public class XhtmlHelper {
42  
43  
44      // ------------------------------------------------------ Manifest Constants
45  
46  
47      /***
48       * <p>The prefix to the request attributes that we will use to keep track
49       * of whether a particular resource has been linked already.</p>
50       */
51      protected static final String PREFIX = "org.apache.shale.remoting.LINKED";
52  
53  
54      // ------------------------------------------------------ Instance Variables
55  
56  
57      /***
58       * <p>Helper to retrieve the {@link Mappings} instance for this application.</p>
59       */
60      private MappingsHelper helper = new MappingsHelper();
61  
62  
63      // ---------------------------------------------------------- Public Methods
64  
65  
66      /***
67       * <p>Render a link to a JavaScript resource at the specified resource
68       * identifier.</p>
69       *
70       * @param context <code>FacesContext</code> for the current request
71       * @param component <code>UIComponent</code> being rendered
72       * @param writer <code>ResponseWriter</code> to render output to
73       * @param mechanism Mechanism used to retrieve the specified resource
74       *  (used to select the appropriate {@link Processor}
75       * @param resourceId Resource identifier used to retrieve the requested
76       *  JavaScript resource
77       *
78       * @exception IllegalArgumentException if <code>mechanism</code> or
79       *  <code>resourceId</code> is <code>null</code>
80       * @exception IllegalStateException if a configuration error prevents
81       *  the mapping of this resource identifier to a corresponding URI
82       * @exception IOException if an input/output error occurs
83       */
84      public void linkJavascript(FacesContext context, UIComponent component,
85                                 ResponseWriter writer,
86                                 Mechanism mechanism, String resourceId)
87          throws IOException {
88  
89          linkJavascript(context, component, writer,
90                         mechanism, resourceId, "text/javascript");
91  
92      }
93  
94  
95      /***
96       * <p>Render a link to a JavaScript resource at the specified resource
97       * identifier.</p>
98       *
99       * @param context <code>FacesContext</code> for the current request
100      * @param component <code>UIComponent</code> being rendered
101      * @param writer <code>ResponseWriter</code> to render output to
102      * @param mechanism Mechanism used to retrieve the specified resource
103      *  (used to select the appropriate {@link Processor}
104      * @param resourceId Resource identifier used to retrieve the requested
105      *  JavaScript resource
106      * @param contentType Content type to specify (for pulling specific
107      *  versions of JavaScript resources)
108      *
109      * @exception IllegalArgumentException if <code>mechanism</code> or
110      *  <code>resourceId</code> is <code>null</code>
111      * @exception IllegalStateException if a configuration error prevents
112      *  the mapping of this resource identifier to a corresponding URI
113      * @exception IOException if an input/output error occurs
114      */
115     public void linkJavascript(FacesContext context, UIComponent component,
116                                ResponseWriter writer,
117                                Mechanism mechanism, String resourceId,
118                                String contentType) throws IOException {
119 
120         if (linked(context, resourceId)) {
121             return;
122         }
123 
124         writer.startElement("script", component);
125         writer.writeAttribute("type", contentType, null);
126         writer.writeURIAttribute("src", mapResourceId(context, mechanism, resourceId), null);
127         writer.endElement("script");
128         writer.write("\n");
129 
130         link(context, resourceId);
131 
132     }
133 
134 
135     /***
136      * <p>Render a link to a CSS stylesheet at the specified resource
137      * identifier.</p>
138      *
139      * @param context <code>FacesContext</code> for the current request
140      * @param component <code>UIComponent</code> being rendered
141      * @param writer <code>ResponseWriter</code> to render output to
142      * @param mechanism Mechanism used to retrieve the specified resource
143      *  (used to select the appropriate {@link Processor}
144      * @param resourceId Resource identifier used to retrieve the requested
145      *  stylesheet resource
146      *
147      * @exception IllegalArgumentException if <code>mechanism</code> or
148      *  <code>resourceId</code> is <code>null</code>
149      * @exception IllegalStateException if a configuration error prevents
150      *  the mapping of this resource identifier to a corresponding URI
151      * @exception IOException if an input/output error occurs
152      */
153     public void linkStylesheet(FacesContext context, UIComponent component,
154                                ResponseWriter writer,
155                                Mechanism mechanism, String resourceId)
156         throws IOException {
157 
158         if (linked(context, resourceId)) {
159             return;
160         }
161 
162         writer.startElement("link", component);
163         writer.writeAttribute("type", "text/css", null);
164         writer.writeAttribute("rel", "stylesheet", null);
165         writer.writeURIAttribute("href", mapResourceId(context, mechanism, resourceId), null);
166         writer.endElement("link");
167         writer.write("\n");
168 
169         link(context, resourceId);
170 
171     }
172 
173 
174 
175     /***
176      * <p>Map the specified resource identifier to a request URL, taking into
177      * account the mappings for the specified mechanism and the servlet mapping
178      * for the JavaServer Faces controller servlet.</p>
179      *
180      * @param context <code>FacesContext</code> for the current request
181      * @param mechanism Requested mechanism
182      * @param resourceId Resource identifier to be mapped
183      *
184      * @exception IllegalArgumentException if <code>mechanism</code> or
185      *  <code>resourceId</code> is <code>null</code>
186      * @exception IllegalStateException if a configuration error prevents
187      *  the mapping of this resource identifier to a corresponding URI
188      */
189     public String mapResourceId(FacesContext context, Mechanism mechanism,
190                                 String resourceId) {
191 
192         // Validate our incoming parameters
193         if (resourceId == null) {
194             throw new IllegalArgumentException
195                     (resourceBundle(context).getString("xhtml.noResourceId"));
196         }
197         if (mechanism == null) {
198             throw new IllegalArgumentException
199                     (resourceBundle(context).getString("xhtml.noMechanism"));
200         }
201 
202         // If we are running inside a design time tool, the runtime
203         // initialization might not have been performed.  Therefore,
204         // just return the incoming resource identifier unchanged, sinc
205         // it is not going to be executed anyway.
206         if (Beans.isDesignTime()) {
207             return resourceId;
208         }
209 
210         // Acquire a reference to the Mappings instance for this application
211         Mappings mappings = helper.getMappings(context);
212         if (mappings == null) {
213             throw new IllegalStateException
214                     (resourceBundle(context).getString("xhtml.noMappings"));
215         }
216 
217         // Acquire the Mapping instance to be used for the requesed mechanism
218         List list = mappings.getMappings();
219         if (list == null) {
220             list = new ArrayList();
221         }
222         Iterator instances = list.iterator();
223         Mapping mapping = null;
224         while (instances.hasNext()) {
225             Mapping instance = (Mapping) instances.next();
226             if (mechanism == instance.getMechanism()) {
227                 mapping = instance;
228                 break;
229             }
230         }
231         if (mapping == null) {
232             throw new IllegalArgumentException(mechanism.toString());
233         }
234 
235         // Ask this Mapping to map the resource identifier appropriately
236         return mapping.mapResourceId(context, resourceId);
237 
238     }
239 
240 
241     // ------------------------------------------------------- Protected Methods
242 
243 
244     /***
245      * <p>Mark the specified resource identifier as having already been
246      * linked in the current request.</p>
247      *
248      * @param context <code>FacesContext</code> for the current request
249      * @param resourceId Resource identifier to mark as having been linked
250      */
251     protected void link(FacesContext context, String resourceId) {
252 
253         context.getExternalContext().getRequestMap().
254                 put(PREFIX + resourceId, Boolean.TRUE);
255 
256     }
257 
258 
259     /***
260      * <p>Return <code>true</code> if the specified resource identifier has
261      * already been linked in the current request, and should therefore not
262      * be linked again.</p>
263      *
264      * @param context <code>FacesContext</code> for the current request
265      * @param resourceId Resource identifier to check for prior linking
266      */
267     protected boolean linked(FacesContext context, String resourceId) {
268 
269         return context.getExternalContext().getRequestMap().
270                 containsKey(PREFIX + resourceId);
271 
272     }
273 
274 
275     /***
276      * <p>Return the localized resource bundle we should use to generate
277      * exception or log messages for this request.</p>
278      *
279      * @param context <code>FacesContext</code> for this request
280      */
281     protected ResourceBundle resourceBundle(FacesContext context) {
282 
283         return ResourceBundle.getBundle("org.apache.shale.remoting.Bundle",
284                                         context.getViewRoot().getLocale(),
285                                         Thread.currentThread().getContextClassLoader());
286 
287     }
288 
289 
290 }