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.application.FacesMessage;
26  import javax.faces.application.ProjectStage;
27  import javax.faces.application.Resource;
28  import javax.faces.component.UIComponent;
29  import javax.faces.context.FacesContext;
30  import javax.faces.context.ResponseWriter;
31  import javax.faces.event.ComponentSystemEvent;
32  import javax.faces.event.ComponentSystemEventListener;
33  import javax.faces.event.ListenerFor;
34  import javax.faces.event.PostAddToViewEvent;
35  import javax.faces.render.Renderer;
36  import javax.faces.view.Location;
37  
38  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderer;
39  import org.apache.myfaces.context.RequestViewContext;
40  import org.apache.myfaces.shared.config.MyfacesConfig;
41  import org.apache.myfaces.shared.renderkit.JSFAttr;
42  import org.apache.myfaces.shared.renderkit.RendererUtils;
43  import org.apache.myfaces.shared.renderkit.html.HTML;
44  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
45  import org.apache.myfaces.shared.util.ExternalContextUtils;
46  import org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage;
47  import org.apache.myfaces.view.facelets.el.CompositeComponentELUtils;
48  import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
49  
50  /**
51   * Renderer used by h:outputStylesheet component 
52   * 
53   * Note: originally this component required PostBuildComponentTreeOnRestoreViewEvent,
54   * but that is no longer true because a tag handler implementing RelocatableResourceHandler
55   * interface was associated, so the component is really marked and refreshed.
56   * 
57   * @since 2.0
58   * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
59   * @version $Revision: 1569988 $ $Date: 2014-02-19 18:51:55 -0500 (Wed, 19 Feb 2014) $
60   */
61  @JSFRenderer(renderKitId = "HTML_BASIC", family = "javax.faces.Output", type = "javax.faces.resource.Stylesheet")
62  @ListenerFor(systemEventClass = PostAddToViewEvent.class)
63  public class HtmlStylesheetRenderer extends Renderer implements
64      ComponentSystemEventListener
65  {
66      //private static final Log log = LogFactory.getLog(HtmlStylesheetRenderer.class);
67      private static final Logger log = Logger.getLogger(HtmlStylesheetRenderer.class.getName());
68      
69      private static final String IS_BUILDING_INITIAL_STATE = "javax.faces.IS_BUILDING_INITIAL_STATE";
70      
71      public void processEvent(ComponentSystemEvent event)
72      {
73          if (event instanceof PostAddToViewEvent)
74          {
75              UIComponent component = event.getComponent();
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              // If this is an ajax request and the view is being refreshed and a PostAddToViewEvent
91              // was propagated to relocate this resource, means the header must be refreshed.
92              // Note ajax request does not occur on non postback requests.
93              
94              if (!ExternalContextUtils.isPortlet(facesContext.getExternalContext()) &&
95                  facesContext.getPartialViewContext().isAjaxRequest() )
96              {
97                  boolean isBuildingInitialState = facesContext.getAttributes().
98                      containsKey(IS_BUILDING_INITIAL_STATE);
99                  // The next condition takes into account the current request is an ajax request. 
100                 boolean isPostAddToViewEventAfterBuildInitialState = 
101                     !isBuildingInitialState ||
102                     (isBuildingInitialState && 
103                             FaceletViewDeclarationLanguage.isRefreshingTransientBuild(facesContext));
104                 if (isPostAddToViewEventAfterBuildInitialState &&
105                         MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).
106                             isStrictJsf2RefreshTargetAjax())
107                 {
108                     //!(component.getParent() instanceof ComponentResourceContainer)
109                     RequestViewContext requestViewContext = RequestViewContext.getCurrentInstance(facesContext);
110                     requestViewContext.setRenderTarget("head", true);
111                 }
112             }
113             facesContext.getViewRoot().addComponentResource(facesContext,
114                         component, "head");
115         }
116     }
117     
118     @Override
119     public boolean getRendersChildren()
120     {
121         return true;
122     }
123 
124     @Override
125     public void encodeChildren(FacesContext facesContext, UIComponent component)
126             throws IOException
127     {
128         if (facesContext == null)
129         {
130             throw new NullPointerException("context");
131         }
132         if (component == null)
133         {
134             throw new NullPointerException("component");
135         }
136 
137         Map<String, Object> componentAttributesMap = component.getAttributes();
138         String resourceName = (String) componentAttributesMap.get(JSFAttr.NAME_ATTR);
139         boolean hasChildren = component.getChildCount() > 0;
140         
141         if (resourceName != null && (!"".equals(resourceName)) )
142         {
143             if (hasChildren)
144             {
145                 log.info("Component with resourceName "+ resourceName + 
146                         " and child components found. Child components will be ignored.");
147             }
148         }
149         else
150         {
151             if (hasChildren)
152             {
153                 ResponseWriter writer = facesContext.getResponseWriter();
154                 writer.startElement(HTML.STYLE_ELEM, component);
155                 writer.writeAttribute(HTML.TYPE_ATTR, HTML.STYLE_TYPE_TEXT_CSS, null);
156                 RendererUtils.renderChildren(facesContext, component);
157                 writer.endElement(HTML.STYLE_ELEM);
158             }
159             else
160             {
161                 if (!facesContext.isProjectStage(ProjectStage.Production))
162                 {
163                     facesContext.addMessage(component.getClientId(facesContext), 
164                             new FacesMessage("Component with no name and no body content, so nothing rendered."));
165                 }
166             }            
167         }
168     }
169     
170     @Override
171     public void encodeEnd(FacesContext facesContext, UIComponent component)
172             throws IOException
173     {
174         super.encodeEnd(facesContext, component); //check for NP
175         
176         Map<String, Object> componentAttributesMap = component.getAttributes();
177         String resourceName = (String) componentAttributesMap.get(JSFAttr.NAME_ATTR);
178         String libraryName = (String) componentAttributesMap.get(JSFAttr.LIBRARY_ATTR);
179 
180         if (resourceName == null)
181         {
182             //log.warn("Trying to encode resource represented by component" + 
183             //        component.getClientId() + " without resourceName."+
184             //        " It will be silenty ignored.");
185             return;
186         }
187         if ("".equals(resourceName))
188         {
189             return;
190         }
191         
192         String additionalQueryParams = null;
193         int index = resourceName.indexOf('?');
194         if (index >= 0)
195         {
196             additionalQueryParams = resourceName.substring(index + 1);
197             resourceName = resourceName.substring(0, index);
198         }
199         
200         Resource resource;
201         if (libraryName == null)
202         {
203             if (ResourceUtils.isRenderedStylesheet(facesContext, libraryName, resourceName))
204             {
205                 //Resource already founded
206                 return;
207             }
208             resource = facesContext.getApplication().getResourceHandler()
209                     .createResource(resourceName);
210         }
211         else
212         {
213             if (ResourceUtils.isRenderedStylesheet(facesContext, libraryName, resourceName))
214             {
215                 //Resource already founded
216                 return;
217             }
218             resource = facesContext.getApplication().getResourceHandler()
219                     .createResource(resourceName, libraryName);
220 
221         }
222         
223         if (resource == null)
224         {
225             //no resource found
226             log.warning("Resource referenced by resourceName "+ resourceName +
227                     (libraryName == null ? "" : " and libraryName " + libraryName) +
228                     " not found in call to ResourceHandler.createResource."+
229                     " It will be silenty ignored.");
230             return;
231         }
232         else
233         {
234             if (ResourceUtils.isRenderedStylesheet(facesContext, resource.getLibraryName(), resource.getResourceName()))
235             {
236                 //Resource already founded
237                 return;
238             }
239 
240             // Rendering resource
241             ResourceUtils.markStylesheetAsRendered(facesContext, libraryName, resourceName);
242             ResourceUtils.markStylesheetAsRendered(facesContext, resource.getLibraryName(), resource.getResourceName());
243             ResponseWriter writer = facesContext.getResponseWriter();
244             writer.startElement(HTML.LINK_ELEM, component);
245             writer.writeAttribute(HTML.REL_ATTR, HTML.STYLESHEET_VALUE,null );
246             String media = (String) component.getAttributes().get("media");
247             writer.writeAttribute("media", media == null ? "screen" : media ,null );
248             writer.writeAttribute(HTML.TYPE_ATTR, 
249                     (resource.getContentType() == null ? HTML.STYLE_TYPE_TEXT_CSS
250                             : resource.getContentType()) , null);
251             String path = resource.getRequestPath();
252             if (additionalQueryParams != null)
253             {
254                 path = path + ((path.indexOf('?') >= 0) ? "&amp;" : "?") + additionalQueryParams;
255             }
256             writer.writeURIAttribute(HTML.HREF_ATTR,
257                     facesContext.getExternalContext().encodeResourceURL(path), null);
258             writer.endElement(HTML.LINK_ELEM);
259         }
260     }
261 }