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.view.facelets.tag.ui;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.LinkedHashMap;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Map.Entry;
27  
28  import javax.faces.component.UIComponent;
29  import javax.faces.component.UIComponentBase;
30  import javax.faces.context.FacesContext;
31  import javax.faces.context.ResponseWriter;
32  import javax.servlet.http.HttpServletResponse;
33  
34  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
35  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
36  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
37  import org.apache.myfaces.renderkit.ErrorPageWriter;
38  import org.apache.myfaces.view.facelets.util.FastWriter;
39  
40  /**
41   * The debug tag will capture the component tree and variables when it is encoded, 
42   * storing the data for retrieval later. You may launch the debug window at any time 
43   * from your browser by pressing 'CTRL' + 'SHIFT' + 'D' (by default).
44   * 
45   * The debug tag doesn't need to be used with the facelet.DEVELOPMENT parameter.
46   * The best place to put this tag is in your site's main template where it can be 
47   * enabled/disabled across your whole application. 
48   * 
49   * If your application uses multiple windows, you might want to assign different 
50   * hot keys to each one.
51   * 
52   * @author Jacob Hookom
53   * @version $Id: UIDebug.java 1552844 2013-12-20 23:41:33Z lu4242 $
54   */
55  @JSFComponent(name="ui:debug")
56  @JSFJspProperty(name = "binding", tagExcluded=true)
57  public final class UIDebug extends UIComponentBase
58  {
59      public static final String COMPONENT_TYPE = "facelets.ui.Debug";
60      public static final String COMPONENT_FAMILY = "facelets";
61      public static final String DEFAULT_HOTKEY = "D";
62      
63      private static final String KEY = "facelets.ui.DebugOutput";
64      
65      private static long nextId = System.currentTimeMillis();
66  
67      private String _hotkey = DEFAULT_HOTKEY;
68  
69      public UIDebug()
70      {
71          setTransient(true);
72          setRendered(true);
73          setRendererType(null);
74      }
75  
76      public String getFamily()
77      {
78          return COMPONENT_FAMILY;
79      }
80  
81      public List<UIComponent> getChildren()
82      {
83          return new ArrayList<UIComponent>()
84          {
85              public boolean add(UIComponent o)
86              {
87                  throw new IllegalStateException("<ui:debug> does not support children");
88              }
89  
90              public void add(int index, UIComponent o)
91              {
92                  throw new IllegalStateException("<ui:debug> does not support children");
93              }
94          };
95      }
96  
97      public void encodeBegin(FacesContext faces) throws IOException
98      {
99          boolean partialRequest = faces.getPartialViewContext().isPartialRequest();
100         
101         String actionId = faces.getApplication().getViewHandler()
102                 .getActionURL(faces, faces.getViewRoot().getViewId());
103         
104         StringBuilder sb = new StringBuilder(512);
105         sb.append("<script language=\"javascript\" type=\"text/javascript\">\n");
106         if (!partialRequest)
107         {
108             sb.append("//<![CDATA[\n");
109         }
110         sb.append("function faceletsDebug(URL) { day = new Date(); id = day.getTime(); eval(\"page\" + id + \" "
111                   + "= window.open(URL, '\" + id + \"', 'toolbar=0,scrollbars=1,location=0,statusbar=0,menubar=0,"
112                   + "resizable=1,width=800,height=600,left = 240,top = 212');\"); };");
113         sb.append("var faceletsOrigKeyup = document.onkeyup; document.onkeyup = function(e) { "
114                   + "if (window.event) e = window.event; if (String.fromCharCode(e.keyCode) == '"
115                   + this.getHotkey() + "' & e.shiftKey & e.ctrlKey) faceletsDebug('");
116         sb.append(actionId);
117         
118         int index = actionId.indexOf ("?");
119         if (index != -1)
120         {
121             sb.append('&');
122         }
123         else
124         {
125             sb.append('?');
126         }
127         sb.append(KEY);
128         sb.append('=');
129         sb.append(writeDebugOutput(faces));
130         sb.append("'); else if (faceletsOrigKeyup) faceletsOrigKeyup(e); };\n");
131         if (!partialRequest)
132         {
133             sb.append("//]]>\n");
134         }
135         sb.append("</script>\n");
136 
137         ResponseWriter writer = faces.getResponseWriter();
138         writer.write(sb.toString());
139     }
140 
141     @SuppressWarnings("unchecked")
142     private static String writeDebugOutput(FacesContext faces) throws IOException
143     {
144         FastWriter fw = new FastWriter();
145         ErrorPageWriter.debugHtml(fw, faces);
146 
147         Map<String, Object> session = faces.getExternalContext().getSessionMap();
148         Map<String, String> debugs = (Map<String, String>) session.get(KEY);
149         if (debugs == null)
150         {
151             debugs = new LinkedHashMap<String, String>()
152             {
153                 protected boolean removeEldestEntry(Entry<String, String> eldest)
154                 {
155                     return (this.size() > 5);
156                 }
157             };
158             
159             session.put(KEY, debugs);
160         }
161         
162         String id = String.valueOf(nextId++);
163         
164         debugs.put(id, fw.toString());
165         
166         return id;
167     }
168 
169     @SuppressWarnings("unchecked")
170     private static String fetchDebugOutput(FacesContext faces, String id)
171     {
172         Map<String, Object> session = faces.getExternalContext().getSessionMap();
173         Map<String, String> debugs = (Map<String, String>) session.get(KEY);
174         if (debugs != null)
175         {
176             return debugs.get(id);
177         }
178         
179         return null;
180     }
181 
182     public static boolean debugRequest(FacesContext faces)
183     {
184         String id = (String) faces.getExternalContext().getRequestParameterMap().get(KEY);
185         if (id != null)
186         {
187             Object resp = faces.getExternalContext().getResponse();
188             if (!faces.getResponseComplete() && resp instanceof HttpServletResponse)
189             {
190                 try
191                 {
192                     HttpServletResponse httpResp = (HttpServletResponse) resp;
193                     String page = fetchDebugOutput(faces, id);
194                     if (page != null)
195                     {
196                         httpResp.setContentType("text/html");
197                         httpResp.getWriter().write(page);
198                     }
199                     else
200                     {
201                         httpResp.setContentType("text/plain");
202                         httpResp.getWriter().write("No Debug Output Available");
203                     }
204                     httpResp.flushBuffer();
205                     faces.responseComplete();
206                 }
207                 catch (IOException e)
208                 {
209                     return false;
210                 }
211                 
212                 return true;
213             }
214         }
215         
216         return false;
217     }
218 
219     @JSFProperty(tagExcluded=true)
220     @Override
221     public String getId()
222     {
223         // TODO Auto-generated method stub
224         return super.getId();
225     }
226 
227     /**
228      * The hot key to use in combination with 'CTRL' + 'SHIFT' to launch the debug window. 
229      * By default, when the debug tag is used, you may launch the debug window with 
230      * 'CTRL' + 'SHIFT' + 'D'. This value cannot be an EL expression.
231      * 
232      * @return
233      */
234     @JSFProperty
235     public String getHotkey()
236     {
237         return _hotkey;
238     }
239 
240     public void setHotkey(String hotkey)
241     {
242         _hotkey = (hotkey != null) ? hotkey.toUpperCase() : "";
243     }
244 }