Wiki source code of Click to Debug

Last modified by Kieran Kelleher on 2009/08/12 14:33

Show last authors
1 == What It Is ==
2
3 Click to Debug is a [[doc:documentation.Home.WOLips Tutorials.Click to Open.WebHome]] extension that allows you to toggle binding debugging from the UI while your application is running.
4
5 == What You Need ==
6
7 See the [[What You Need>>doc:documentation.Home.WOLips Tutorials.Click to Open.WebHome||anchor="What You Need"]] section of the Click to Open documentation if you are not already using Click to to Open.
8
9 == Getting Set Up ==
10
11 See the [[Getting Set Up>>doc:documentation.Home.WOLips Tutorials.Click to Open.WebHome||anchor="Getting Set Up"]] section of the Click to Open documentation if you are not already using Click to to Open.
12
13 Add this line to the {{code language="none"}}Properties{{/code}} file in your application:
14
15 {{code}}
16
17 ognl.debugSupport=true
18
19 {{/code}}
20
21 === Add Support to Application ===
22
23 If your Application.java class extends (directly or indirectly) Wonder's ERXApplication, you can skip this step. Otherwise, add this to your Application() constructor, or a method that runs before requests are processed::
24
25 {{code 0="java"}}
26
27 NSNotificationCenter.defaultCenter().addObserver(this,
28 ERXSelectorUtilities.notificationSelector("applicationDidHandleRequest"),
29 WOApplication.ApplicationDidDispatchRequestNotification, null);
30
31 {{/code}}
32
33 Then add this method to handle this notification:
34
35 {{code 0="java"}}
36
37 /**
38 * When request is finished, we remove the context from thread local storage.
39 *
40 * @see #createContextForRequest(WORequest)
41 * @param n notification
42 */
43 public void applicationDidHandleRequest(NSNotification n) {
44 ERXWOContext.setCurrentContext(null);
45 ERXThreadStorage.removeValueForKey(ERXWOContext.CONTEXT_DICTIONARY_KEY);
46 }
47
48 {{/code}}
49
50 The next part is a method that sets ERXWOContext.currentContext() when a request is dispatched:
51
52 {{code 0="java"}}
53
54 /**
55 * When a context is created we push it into thread local storage.
56 *
57 * @see #applicationDidHandleRequest(NSNotification)
58 * @param request the request
59 * @return the newly created context
60 */
61 public WOContext createContextForRequest(WORequest request) {
62 WOContext context = super.createContextForRequest(request);
63 // We only want to push in the context the first time it is
64 // created, ie we don't want to lose the current context
65 // when we create a context for an error page.
66 if (ERXWOContext.currentContext() == null) {
67 ERXWOContext.setCurrentContext(context);
68 }
69 return context;
70 }
71
72 {{/code}}
73
74 And finally, add this code to support Click to Debug:
75
76 {{code 0="java"}}
77
78 protected void _debugValueForDeclarationNamed(WOComponent component, String verb, String aDeclarationName,
79 String aDeclarationType, String aBindingName,
80 String anAssociationDescription, Object aValue) {
81 if (aValue instanceof String) {
82 StringBuffer stringbuffer = new StringBuffer(((String) aValue).length() + 2);
83 stringbuffer.append('"');
84 stringbuffer.append(aValue);
85 stringbuffer.append('"');
86 aValue = stringbuffer;
87 }
88 if (aDeclarationName.startsWith("_")) {
89 aDeclarationName = "[inline]";
90 }
91
92 StringBuffer sb = new StringBuffer();
93
94 //NSArray<WOComponent> componentPath = ERXWOContext._componentPath(ERXWOContext.currentContext());
95 //componentPath.lastObject()
96 //WOComponent lastComponent = ERXWOContext.currentContext().component();
97 String lastComponentName = component.name().replaceFirst(".*\\.", "");
98 sb.append(lastComponentName);
99
100 sb.append(verb);
101
102 if (!aDeclarationName.startsWith("_")) {
103 sb.append(aDeclarationName);
104 sb.append(":");
105 }
106 sb.append(aDeclarationType);
107
108 sb.append(" { ");
109 sb.append(aBindingName);
110 sb.append("=");
111
112 String valueStr = aValue != null ? aValue.toString() : "null";
113 if (anAssociationDescription.startsWith("class ")) {
114 sb.append(valueStr);
115 sb.append("; }");
116 }
117 else {
118 sb.append(anAssociationDescription);
119 sb.append("; } value ");
120 sb.append(valueStr);
121 }
122
123 NSLog.debug.appendln(sb.toString());
124 }
125
126 /**
127 * The set of component names that have binding debug enabled
128 */
129 private NSMutableSet<String> _debugComponents = new NSMutableSet<String>();
130
131 /**
132 * Little bit better binding debug output than the original.
133 */
134 @Override
135 public void logTakeValueForDeclarationNamed(String aDeclarationName, String aDeclarationType,
136 String aBindingName, String anAssociationDescription, Object aValue) {
137 WOComponent component = ERXWOContext.currentContext().component();
138 if (component.parent() != null) {
139 component = component.parent();
140 }
141 _debugValueForDeclarationNamed(component, " ==> ", aDeclarationName,
142 aDeclarationType, aBindingName, anAssociationDescription, aValue);
143 }
144
145 /**
146 * Little bit better binding debug output than the original.
147 */
148 @Override
149 public void logSetValueForDeclarationNamed(String aDeclarationName, String aDeclarationType,
150 String aBindingName, String anAssociationDescription, Object aValue) {
151 WOComponent component = ERXWOContext.currentContext().component();
152 if (component.parent() != null) {
153 component = component.parent();
154 }
155 _debugValueForDeclarationNamed(component, " <== ", aDeclarationName, aDeclarationType,
156 aBindingName, anAssociationDescription, aValue);
157 }
158
159 /**
160 * Turns on/off binding debugging for the given component. Binding debugging requires using the WOOgnl
161 * template parser and setting ognl.debugSupport=true.
162 *
163 * @param debugEnabled whether or not to enable debugging
164 * @param componentName the component name to enable debugging for
165 */
166 public void setDebugEnabledForComponent(boolean debugEnabled, String componentName) {
167 if (debugEnabled) {
168 _debugComponents.addObject(componentName);
169 }
170 else {
171 _debugComponents.removeObject(componentName);
172 }
173 }
174
175 /**
176 * Returns whether or not binding debugging is enabled for the given component
177 *
178 * @param componentName the component name
179 * @return whether or not binding debugging is enabled for the given componen
180 */
181 public boolean debugEnabledForComponent(String componentName) {
182 return _debugComponents.containsObject(componentName);
183 }
184
185 /**
186 * Turns off binding debugging for all components.
187 */
188 public void clearDebugEnabledForAllComponents() {
189 _debugComponents.removeAllObjects();
190 }
191
192 {{/code}}
193
194 == Using Click to Debug ==
195
196 Run your application and look in the lower, left hand corner. You should see a link like this:
197
198 [[image:attach:documentation.Home.WOLips Tutorials.Click to Open.WebHome@ClickToOpenLink.png]]
199
200 If you don't, check that the page has the WOLToolBar on it and that the {{code language="none"}}er.component.clickToOpen{{/code}} property is set to true and the {{code language="none"}}er.extensions.ERXApplication.developmentMode{{/code}} property is set to true.
201
202 \\
203
204 Click on this component to open the Click to Open UI:
205
206 [[image:attach:documentation.Home.WOLips Tutorials.Click to Open.WebHome@WOLipsToolbar.png]]
207
208 **EditDisplayAd** is the page in the browser. Click on this link to open this page in Eclipse.
209
210 \\
211
212 If you are looking for a sub-component of this page, click on the **Click to Debug** link. As you move your mouse over the page, the bread crumb of components will change to show you where you are. Just click to turn binding debug on (or off if it is on) for the component under the mouse in Eclipse. It is that easy!
213
214 [[image:attach:ClickToDebugInAction.png]]
215
216 \\
217
218 With binding debug on, you'll get output like:
219
220 {{code}}
221
222 DEBUG NSLog - HatchViewTaskPage ==> [inline]:HatchEditTask { task=task; } value <com.mdimension.mdtask.model.Task pk:"1027787">
223 DEBUG NSLog - HatchViewTaskPage <== [inline]:HatchEditTask { task=task; } value <com.mdimension.mdtask.model.Task pk:"1027787">
224 DEBUG NSLog - HatchViewTaskPage ==> [inline]:HatchEditTask { task=task; } value <com.mdimension.mdtask.model.Task pk:"1027787">
225 DEBUG NSLog - HatchViewTaskPage <== [inline]:HatchEditTask { task=task; } value <com.mdimension.mdtask.model.Task pk:"1027787">
226 DEBUG NSLog - HatchViewTaskPage ==> [inline]:HatchEditTask { task=task; } value <com.mdimension.mdtask.model.Task pk:"1027787">
227 DEBUG NSLog - HatchViewTaskPage <== [inline]:HatchEditTask { task=task; } value null
228 DEBUG NSLog - HatchViewTaskPage ==> [inline]:HatchEditTask { task=task; } value null
229 DEBUG NSLog - HatchViewTaskPage <== [inline]:HatchEditTask { task=task; } value null
230
231 {{/code}}
232
233 **Note**: A prefix like {{code language="none"}}Dec 13 11:00:29 MDTask[WOL:62934] (ERXNSLogLog4jBridge.java:46){{/code}} was removed from each line above to make this easier to read.
234
235 So what that's showing is HatchViewTaskPage component is pushing the task binding into the HatchEditTask component with the value Task
236 1027787, then HatchEditTask component is pushing the binding back up. So you can see here that 6th line, HatchEditTask component is pushing a null binding back out (which, in this case, was causing a problem I was trying to find).