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 1552845 2013-12-20 23:41:53Z 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 + \" = window.open(URL, '\" + id + \"', 'toolbar=0,scrollbars=1,location=0,statusbar=0,menubar=0,resizable=1,width=800,height=600,left = 240,top = 212');\"); };");
111         sb.append("var faceletsOrigKeyup = document.onkeyup; document.onkeyup = function(e) { if (window.event) e = window.event; if (String.fromCharCode(e.keyCode) == '"
112                 + this.getHotkey() + "' & e.shiftKey & e.ctrlKey) faceletsDebug('");
113         sb.append(actionId);
114         
115         int index = actionId.indexOf ("?");
116         if (index != -1)
117         {
118             sb.append('&');
119         }
120         else
121         {
122             sb.append('?');
123         }
124         sb.append(KEY);
125         sb.append('=');
126         sb.append(writeDebugOutput(faces));
127         sb.append("'); else if (faceletsOrigKeyup) faceletsOrigKeyup(e); };\n");
128         if (!partialRequest)
129         {
130             sb.append("//]]>\n");
131         }
132         sb.append("</script>\n");
133 
134         ResponseWriter writer = faces.getResponseWriter();
135         writer.write(sb.toString());
136     }
137 
138     @SuppressWarnings("unchecked")
139     private static String writeDebugOutput(FacesContext faces) throws IOException
140     {
141         FastWriter fw = new FastWriter();
142         ErrorPageWriter.debugHtml(fw, faces);
143 
144         Map<String, Object> session = faces.getExternalContext().getSessionMap();
145         Map<String, String> debugs = (Map<String, String>) session.get(KEY);
146         if (debugs == null)
147         {
148             debugs = new LinkedHashMap<String, String>()
149             {
150                 protected boolean removeEldestEntry(Entry<String, String> eldest)
151                 {
152                     return (this.size() > 5);
153                 }
154             };
155             
156             session.put(KEY, debugs);
157         }
158         
159         String id = String.valueOf(nextId++);
160         
161         debugs.put(id, fw.toString());
162         
163         return id;
164     }
165 
166     @SuppressWarnings("unchecked")
167     private static String fetchDebugOutput(FacesContext faces, String id)
168     {
169         Map<String, Object> session = faces.getExternalContext().getSessionMap();
170         Map<String, String> debugs = (Map<String, String>) session.get(KEY);
171         if (debugs != null)
172         {
173             return debugs.get(id);
174         }
175         
176         return null;
177     }
178 
179     public static boolean debugRequest(FacesContext faces)
180     {
181         String id = (String) faces.getExternalContext().getRequestParameterMap().get(KEY);
182         if (id != null)
183         {
184             Object resp = faces.getExternalContext().getResponse();
185             if (!faces.getResponseComplete() && resp instanceof HttpServletResponse)
186             {
187                 try
188                 {
189                     HttpServletResponse httpResp = (HttpServletResponse) resp;
190                     String page = fetchDebugOutput(faces, id);
191                     if (page != null)
192                     {
193                         httpResp.setContentType("text/html");
194                         httpResp.getWriter().write(page);
195                     }
196                     else
197                     {
198                         httpResp.setContentType("text/plain");
199                         httpResp.getWriter().write("No Debug Output Available");
200                     }
201                     httpResp.flushBuffer();
202                     faces.responseComplete();
203                 }
204                 catch (IOException e)
205                 {
206                     return false;
207                 }
208                 
209                 return true;
210             }
211         }
212         
213         return false;
214     }
215 
216     @JSFProperty(tagExcluded=true)
217     @Override
218     public String getId()
219     {
220         // TODO Auto-generated method stub
221         return super.getId();
222     }
223 
224     /**
225      * The hot key to use in combination with 'CTRL' + 'SHIFT' to launch the debug window. 
226      * By default, when the debug tag is used, you may launch the debug window with 
227      * 'CTRL' + 'SHIFT' + 'D'. This value cannot be an EL expression.
228      * 
229      * @return
230      */
231     @JSFProperty
232     public String getHotkey()
233     {
234         return _hotkey;
235     }
236 
237     public void setHotkey(String hotkey)
238     {
239         _hotkey = (hotkey != null) ? hotkey.toUpperCase() : "";
240     }
241 }