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