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.util;
20  
21  import java.io.IOException;
22  import java.util.List;
23  import java.util.Map;
24  
25  import javax.faces.context.FacesContext;
26  import javax.faces.context.ResponseWriter;
27  import javax.faces.event.PhaseEvent;
28  import javax.faces.event.PhaseId;
29  import javax.faces.event.PhaseListener;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.myfaces.shared_tomahawk.config.MyfacesConfig;
34  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
35  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
36  import org.apache.myfaces.shared_tomahawk.renderkit.html.util.JavascriptUtils;
37  
38  /**
39   * This phase listener puts in the request the javascript code needed to render the dummyForm
40   * and the autoscroll feature.
41   *
42   * The ExtensionsFilter will put this code before the closing &tt;/body> tag.
43   *
44   * @author Bruno Aranda (latest modification by $Author: lu4242 $)
45   * @version $Revision: 778948 $ $Date: 2009-05-26 20:14:09 -0500 (Tue, 26 May 2009) $
46   */
47  public class ExtensionsPhaseListener implements PhaseListener {
48  
49      private static final Log log = LogFactory.getLog(ExtensionsPhaseListener.class);
50  
51  
52      public  static final String ORG_APACHE_MYFACES_MY_FACES_JAVASCRIPT = "org.apache.myfaces.myFacesJavascript";
53      public static final String LISTENERS_MAP = "_MyFaces_inputAjax_listenersMap";
54  
55      public PhaseId getPhaseId()
56      {
57          return PhaseId.RENDER_RESPONSE;
58      }
59  
60      public void beforePhase(PhaseEvent event)
61      {
62      }
63  
64      public void afterPhase(PhaseEvent event)
65      {
66          FacesContext facesContext = event.getFacesContext();
67  
68          try
69          {
70              getJavaScriptCodeAndStoreInRequest(facesContext);
71          } catch (IOException e)
72          {
73              log.error("Exception while rendering extension filter code.",e);
74          }
75      }
76  
77      /**
78       * Creates javascript-code such as the dummy form and the autoscroll javascript, which goes before the closing </body>
79       *
80       * The extension filter will then finally process it and render it into the page.
81       * 
82       * @throws IOException an exception if writer cannot be written to
83       * @param facesContext The current faces-context
84       */
85      private void getJavaScriptCodeAndStoreInRequest(FacesContext facesContext) throws IOException
86      {
87          Object myFacesJavascript = facesContext.getExternalContext().getRequestMap().get(ORG_APACHE_MYFACES_MY_FACES_JAVASCRIPT);
88  
89          if (myFacesJavascript != null)
90          {
91              return;
92          }
93  
94          facesContext.getExternalContext().getRequestMap().put(ORG_APACHE_MYFACES_MY_FACES_JAVASCRIPT, getCodeBeforeBodyEnd(facesContext));
95      }
96      
97      private static String getCodeBeforeBodyEnd(FacesContext facesContext) throws IOException
98      {
99          ResponseWriter responseWriter = facesContext.getResponseWriter();
100         HtmlBufferResponseWriterWrapper writerWrapper = HtmlBufferResponseWriterWrapper
101                     .getInstance(responseWriter);
102         facesContext.setResponseWriter(writerWrapper);
103 
104         writeCodeBeforeBodyEnd(facesContext);
105 
106         //todo: this is just a quick-fix - Sun RI screams if the old responsewriter is null
107         //but how to reset the old response-writer then?
108         if(responseWriter!=null)
109             facesContext.setResponseWriter(responseWriter);
110 
111         //return "<!-- MYFACES JAVASCRIPT -->\n"+writerWrapper.toString()+"\n";
112         return writerWrapper.toString();
113     }
114 
115     /**In case of StreamingAddResource and a documentBody-Tag, this method will be called with the
116      * normal response writer set.
117      *
118      * It will directly render out the javascript-text at the current position (immediately before body-closing).
119      *
120      * In the case of DefaultAddResource, this method will be called with a wrapped response writer - and we'll
121      * buffer the javascript-text in the request, for the ExtensionFilter to catch and render it.
122      *
123      * @param facesContext The current faces-context.
124      * @throws IOException Exception if writing to the output-stream fails.
125      */
126     public static void writeCodeBeforeBodyEnd(FacesContext facesContext) throws IOException
127     {
128         ResponseWriter writer = facesContext.getResponseWriter();
129         
130         MyfacesConfig myfacesConfig = MyfacesConfig.getCurrentInstance(facesContext.getExternalContext());
131         if (myfacesConfig.isDetectJavascript())
132         {
133             if (! JavascriptUtils.isJavascriptDetected(facesContext.getExternalContext()))
134             {
135 
136                 writer.startElement("script",null);
137                 writer.writeAttribute("attr", HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT,null);
138                 StringBuffer script = new StringBuffer();
139                 script.append("document.location.replace('").
140                         append(facesContext.getApplication().getViewHandler().getResourceURL(facesContext, "/_javascriptDetector_")).append("?goto=").append(facesContext.getApplication().getViewHandler().getActionURL(facesContext, facesContext.getViewRoot().getViewId())).append("');");
141                 writer.writeText(script.toString(),null);
142                 writer.endElement(HTML.SCRIPT_ELEM);
143             }
144         }
145 
146         if (myfacesConfig.isAutoScroll())
147         {
148             HtmlRendererUtils.renderAutoScrollFunction(facesContext, writer);
149         }
150 
151         // now write out listeners
152         // todo: change the get entry below to use the static field in Listener if/when the listeners move to Tomahawk from sandbox
153         try
154         {
155             List listeners = (List) facesContext.getExternalContext().getRequestMap().get("org.apache.myfaces.Listener");
156             //System.out.println("listeners: " + listeners);
157             if(listeners != null && listeners.size() > 0){
158                 writer.startElement(HTML.SCRIPT_ELEM,null);
159                 writer.writeAttribute("attr", HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT,null);
160                 StringBuffer buff = new StringBuffer();
161                 String mapName = LISTENERS_MAP;
162                 buff.append("var ").append(mapName).append(" = new Object();\n");
163                 for (int i = 0; i < listeners.size(); i++)
164                 {
165                     Map listenerItem = (Map) listeners.get(i);
166                     String listenerId = (String) listenerItem.get("listenerId");
167                     String listenOn = (String) listenerItem.get("listenOn");
168                     String action = (String) listenerItem.get("action");
169                     String eventType = (String) listenerItem.get("eventType");
170                     // todo: Should use Listener object for more flexibility, but only when it moves to tomahawk
171                     buff.append("var _MyFaces_listenerItem = ").append(mapName).append("['").append(listenOn).append("'];\n");
172                     buff.append("if(!_MyFaces_listenerItem) {\n");
173                     buff.append("    _MyFaces_listenerItem = new Array();\n");
174                     buff.append("    ").append(mapName).append("['").append(listenOn).append("'] = _MyFaces_listenerItem;\n");
175                     buff.append("}\n");
176                     buff.append("var _MyFaces_listener = new Object();\n");
177                     buff.append("_MyFaces_listener['id'] = '").append(listenerId).append("';\n");
178                     buff.append("_MyFaces_listener['action'] = '").append(action).append("';\n");
179                     buff.append("_MyFaces_listener['eventType'] = '").append(eventType).append("';\n");
180                     buff.append("_MyFaces_listenerItem[_MyFaces_listenerItem.length] = _MyFaces_listener;\n");
181                 }
182                 writer.write(buff.toString());
183                 writer.endElement(HTML.SCRIPT_ELEM);
184             }
185         }
186         catch (Exception e)
187         {
188             log.error("Exception while rendering code for listeners.",e);
189         }
190     }
191 }