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