View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.renderkit.html;
20  
21  import java.io.IOException;
22  import java.util.Map;
23  import java.util.logging.Logger;
24  
25  import javax.faces.FacesException;
26  import javax.faces.application.FacesMessage;
27  import javax.faces.application.ProjectStage;
28  import javax.faces.application.Resource;
29  import javax.faces.component.UIComponent;
30  import javax.faces.context.FacesContext;
31  import javax.faces.context.ResponseWriter;
32  import javax.faces.event.ComponentSystemEvent;
33  import javax.faces.event.ComponentSystemEventListener;
34  import javax.faces.event.ListenerFor;
35  import javax.faces.event.PostAddToViewEvent;
36  import javax.faces.event.PreRenderViewEvent;
37  import javax.faces.render.Renderer;
38  import javax.faces.view.Location;
39  
40  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderer;
41  import org.apache.myfaces.context.RequestViewContext;
42  import org.apache.myfaces.shared.config.MyfacesConfig;
43  import org.apache.myfaces.shared.renderkit.JSFAttr;
44  import org.apache.myfaces.shared.renderkit.RendererUtils;
45  import org.apache.myfaces.shared.renderkit.html.HTML;
46  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
47  import org.apache.myfaces.shared.util.ExternalContextUtils;
48  import org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage;
49  import org.apache.myfaces.view.facelets.el.CompositeComponentELUtils;
50  import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
51  
52  /**
53   * Renderer used by h:outputScript component
54   *
55   * @author Leonardo Uribe (latest modification by $Author$)
56   * @version $Revision$ $Date$
57   * @since 2.0
58   */
59  @JSFRenderer(renderKitId = "HTML_BASIC", family = "javax.faces.Output", type = "javax.faces.resource.Script")
60  @ListenerFor(systemEventClass = PostAddToViewEvent.class)
61  public class HtmlScriptRenderer extends Renderer implements ComponentSystemEventListener
62  {
63      //private static final Log log = LogFactory.getLog(HtmlScriptRenderer.class);
64      private static final Logger log = Logger.getLogger(HtmlScriptRenderer.class.getName());
65  
66      private static final String IS_BUILDING_INITIAL_STATE = "javax.faces.IS_BUILDING_INITIAL_STATE";
67  
68      public void processEvent(ComponentSystemEvent event)
69      {
70          if (event instanceof PostAddToViewEvent)
71          {
72              UIComponent component = event.getComponent();
73              String target = (String) component.getAttributes().get(JSFAttr.TARGET_ATTR);
74              if (target != null)
75              {
76                  FacesContext facesContext = FacesContext.getCurrentInstance();
77  
78                  Location location = (Location) component.getAttributes().get(CompositeComponentELUtils.LOCATION_KEY);
79                  if (location != null)
80                  {
81                      UIComponent ccParent
82                              = CompositeComponentELUtils.getCompositeComponentBasedOnLocation(facesContext, location);
83                      if (ccParent != null)
84                      {
85                          component.getAttributes().put(
86                                  CompositeComponentELUtils.CC_FIND_COMPONENT_EXPRESSION,
87                                  ComponentSupport.getFindComponentExpression(facesContext, ccParent));
88                      }
89                  }
90  
91                  // If this is an ajax request and the view is being refreshed and a PostAddToViewEvent
92                  // was propagated to relocate this resource, means the header must be refreshed.
93                  // Note ajax request does not occur on non postback requests.
94                  
95                  if (!ExternalContextUtils.isPortlet(facesContext.getExternalContext()) &&
96                      facesContext.getPartialViewContext().isAjaxRequest())
97                  {
98                      boolean isBuildingInitialState = facesContext.getAttributes().
99                          containsKey(IS_BUILDING_INITIAL_STATE);
100                     // The next condition takes into account the current request is an ajax request. 
101                     boolean isPostAddToViewEventAfterBuildInitialState = 
102                         !isBuildingInitialState ||
103                         (isBuildingInitialState && 
104                                 FaceletViewDeclarationLanguage.isRefreshingTransientBuild(facesContext));
105                     if (isPostAddToViewEventAfterBuildInitialState &&                    
106                         MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).
107                             isStrictJsf2RefreshTargetAjax())
108                     {
109                         //!(component.getParent() instanceof ComponentResourceContainer)
110                         RequestViewContext requestViewContext = RequestViewContext.getCurrentInstance(facesContext);
111                         requestViewContext.setRenderTarget("head", true);
112                     }
113                 }
114 
115                 facesContext.getViewRoot().addComponentResource(facesContext,
116                         component, target);
117             }
118         }
119 
120         if (event instanceof PreRenderViewEvent)
121 
122         {
123             //TODO target check here
124             UIComponent component = event.getComponent();
125             String target = (String) component.getAttributes().get(JSFAttr.TARGET_ATTR);
126             if (target != null)
127             {
128                 FacesContext facesContext = FacesContext.getCurrentInstance();
129                 UIComponent uiTarget = facesContext.getViewRoot().getFacet(target);
130                 if (uiTarget == null)
131                 {
132                     throw new FacesException("Target for component not found");
133                 }
134             }
135         }
136     }
137 
138     @Override
139     public boolean getRendersChildren()
140     {
141         return true;
142     }
143 
144     @Override
145     public void encodeChildren(FacesContext facesContext, UIComponent component)
146             throws IOException
147     {
148         if (facesContext == null)
149         {
150             throw new NullPointerException("context");
151         }
152         if (component == null)
153         {
154             throw new NullPointerException("component");
155         }
156 
157         Map<String, Object> componentAttributesMap = component.getAttributes();
158         String resourceName = (String) componentAttributesMap.get(JSFAttr.NAME_ATTR);
159         boolean hasChildren = component.getChildCount() > 0;
160 
161         if (resourceName != null && (!"".equals(resourceName)))
162         {
163             if (hasChildren)
164             {
165                 log.info("Component with resourceName " + resourceName +
166                         " and child components found. Child components will be ignored.");
167             }
168         }
169         else
170         {
171             if (hasChildren)
172             {
173                 // Children are encoded as usual. Usually the layout is
174                 // <script type="text/javascript">
175                 // ...... some javascript .......
176                 // </script>
177                 ResponseWriter writer = facesContext.getResponseWriter();
178                 writer.startElement(HTML.SCRIPT_ELEM, component);
179                 writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
180                 RendererUtils.renderChildren(facesContext, component);
181                 writer.endElement(HTML.SCRIPT_ELEM);
182             }
183             else
184             {
185                 if (!facesContext.getApplication().getProjectStage().equals(
186                         ProjectStage.Production))
187                 {
188                     facesContext.addMessage(component.getClientId(facesContext),
189                             new FacesMessage("Component with no name and no body content, so nothing rendered."));
190                 }
191             }
192         }
193     }
194 
195     @Override
196     public void encodeEnd(FacesContext facesContext, UIComponent component)
197             throws IOException
198     {
199         super.encodeEnd(facesContext, component); //check for NP
200 
201         Map<String, Object> componentAttributesMap = component.getAttributes();
202         String resourceName = (String) componentAttributesMap.get(JSFAttr.NAME_ATTR);
203         String libraryName = (String) componentAttributesMap.get(JSFAttr.LIBRARY_ATTR);
204 
205         if (resourceName == null)
206         {
207             //log.warn("Trying to encode resource represented by component" +
208             //        component.getClientId() + " without resourceName."+
209             //        " It will be silenty ignored.");
210             return;
211         }
212         if ("".equals(resourceName))
213         {
214             return;
215         }
216 
217         String additionalQueryParams = null;
218         int index = resourceName.indexOf('?');
219         if (index >= 0)
220         {
221             additionalQueryParams = resourceName.substring(index + 1);
222             resourceName = resourceName.substring(0, index);
223         }
224 
225         Resource resource;
226         if (libraryName == null)
227         {
228             if (ResourceUtils.isRenderedScript(facesContext, libraryName, resourceName))
229             {
230                 //Resource already founded
231                 return;
232             }
233             resource = facesContext.getApplication().getResourceHandler()
234                     .createResource(resourceName);
235         }
236         else
237         {
238             if (ResourceUtils.isRenderedScript(facesContext, libraryName, resourceName))
239             {
240                 //Resource already founded
241                 return;
242             }
243             resource = facesContext.getApplication().getResourceHandler()
244                     .createResource(resourceName, libraryName);
245 
246         }
247 
248         if (resource == null)
249         {
250             //no resource found
251             log.warning("Resource referenced by resourceName " + resourceName +
252                     (libraryName == null ? "" : " and libraryName " + libraryName) +
253                     " not found in call to ResourceHandler.createResource." +
254                     " It will be silenty ignored.");
255             return;
256         }
257         else
258         {
259             if (ResourceUtils.isRenderedScript(facesContext, resource.getLibraryName(), resource.getResourceName()))
260             {
261                 //Resource already founded
262                 return;
263             }
264 
265             // Rendering resource
266             ResourceUtils.markScriptAsRendered(facesContext, libraryName, resourceName);
267             ResourceUtils.markScriptAsRendered(facesContext, resource.getLibraryName(), resource.getResourceName());
268             ResponseWriter writer = facesContext.getResponseWriter();
269             writer.startElement(HTML.SCRIPT_ELEM, component);
270 // We can't render the content type, because usually it returns "application/x-javascript"
271 // and this is not compatible with IE. We should force render "text/javascript".
272             writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
273             String path = resource.getRequestPath();
274             if (additionalQueryParams != null)
275             {
276                 path = path + ((path.indexOf('?') >= 0) ? "&amp;" : "?") + additionalQueryParams;
277             }
278             writer.writeURIAttribute(HTML.SRC_ATTR, facesContext.getExternalContext().encodeResourceURL(path), null);
279             writer.endElement(HTML.SCRIPT_ELEM);
280         }
281     }
282 
283 }