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$
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          setRendererType(null);
73      }
74  
75      public String getFamily()
76      {
77          return COMPONENT_FAMILY;
78      }
79  
80      public List<UIComponent> getChildren()
81      {
82          return new ArrayList<UIComponent>()
83          {
84              public boolean add(UIComponent o)
85              {
86                  throw new IllegalStateException("<ui:debug> does not support children");
87              }
88  
89              public void add(int index, UIComponent o)
90              {
91                  throw new IllegalStateException("<ui:debug> does not support children");
92              }
93          };
94      }
95  
96      public void encodeBegin(FacesContext faces) throws IOException
97      {
98          boolean partialRequest = faces.getPartialViewContext().isPartialRequest();
99          
100         String actionId = faces.getApplication().getViewHandler()
101                 .getActionURL(faces, faces.getViewRoot().getViewId());
102         
103         StringBuilder sb = new StringBuilder(512);
104         sb.append("<script language=\"javascript\" type=\"text/javascript\">\n");
105         if (!partialRequest)
106         {
107             sb.append("//<![CDATA[\n");
108         }
109         sb.append("function faceletsDebug(URL) { day = new Date(); id = day.getTime(); eval(\"page\" + id + \" "
110                   + "= window.open(URL, '\" + id + \"', 'toolbar=0,scrollbars=1,location=0,statusbar=0,menubar=0,"
111                   + "resizable=1,width=800,height=600,left = 240,top = 212');\"); };");
112         sb.append("var faceletsOrigKeyup = document.onkeyup; document.onkeyup = function(e) { "
113                   + "if (window.event) e = window.event; if (String.fromCharCode(e.keyCode) == '"
114                   + this.getHotkey() + "' & e.shiftKey & e.ctrlKey) faceletsDebug('");
115         sb.append(actionId);
116         
117         int index = actionId.indexOf('?');
118         if (index != -1)
119         {
120             sb.append('&');
121         }
122         else
123         {
124             sb.append('?');
125         }
126         sb.append(KEY);
127         sb.append('=');
128         sb.append(writeDebugOutput(faces));
129         sb.append("'); else if (faceletsOrigKeyup) faceletsOrigKeyup(e); };\n");
130         if (!partialRequest)
131         {
132             sb.append("//]]>\n");
133         }
134         sb.append("</script>\n");
135 
136         ResponseWriter writer = faces.getResponseWriter();
137         writer.write(sb.toString());
138     }
139 
140     @SuppressWarnings("unchecked")
141     private static String writeDebugOutput(FacesContext faces) throws IOException
142     {
143         FastWriter fw = new FastWriter();
144         ErrorPageWriter.debugHtml(fw, faces);
145 
146         Map<String, Object> session = faces.getExternalContext().getSessionMap();
147         Map<String, String> debugs = (Map<String, String>) session.get(KEY);
148         if (debugs == null)
149         {
150             debugs = new LinkedHashMap<String, String>()
151             {
152                 protected boolean removeEldestEntry(Entry<String, String> eldest)
153                 {
154                     return (this.size() > 5);
155                 }
156             };
157             
158             session.put(KEY, debugs);
159         }
160         
161         String id = String.valueOf(nextId++);
162         
163         debugs.put(id, fw.toString());
164         
165         return id;
166     }
167 
168     @SuppressWarnings("unchecked")
169     private static String fetchDebugOutput(FacesContext faces, String id)
170     {
171         Map<String, Object> session = faces.getExternalContext().getSessionMap();
172         Map<String, String> debugs = (Map<String, String>) session.get(KEY);
173         if (debugs != null)
174         {
175             return debugs.get(id);
176         }
177         
178         return null;
179     }
180 
181     public static boolean debugRequest(FacesContext faces)
182     {
183         String id = (String) faces.getExternalContext().getRequestParameterMap().get(KEY);
184         if (id != null)
185         {
186             Object resp = faces.getExternalContext().getResponse();
187             if (!faces.getResponseComplete() && resp instanceof HttpServletResponse)
188             {
189                 try
190                 {
191                     HttpServletResponse httpResp = (HttpServletResponse) resp;
192                     String page = fetchDebugOutput(faces, id);
193                     if (page != null)
194                     {
195                         httpResp.setContentType("text/html");
196                         httpResp.getWriter().write(page);
197                     }
198                     else
199                     {
200                         httpResp.setContentType("text/plain");
201                         httpResp.getWriter().write("No Debug Output Available");
202                     }
203                     httpResp.flushBuffer();
204                     faces.responseComplete();
205                 }
206                 catch (IOException e)
207                 {
208                     return false;
209                 }
210                 
211                 return true;
212             }
213         }
214         
215         return false;
216     }
217 
218     @JSFProperty(tagExcluded=true)
219     @Override
220     public String getId()
221     {
222         // TODO Auto-generated method stub
223         return super.getId();
224     }
225 
226     /**
227      * The hot key to use in combination with 'CTRL' + 'SHIFT' to launch the debug window. 
228      * By default, when the debug tag is used, you may launch the debug window with 
229      * 'CTRL' + 'SHIFT' + 'D'. This value cannot be an EL expression.
230      * 
231      * @return
232      */
233     @JSFProperty
234     public String getHotkey()
235     {
236         return _hotkey;
237     }
238 
239     public void setHotkey(String hotkey)
240     {
241         _hotkey = (hotkey != null) ? hotkey.toUpperCase() : "";
242     }
243 }