KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > directwebremoting > impl > DefaultRemoter


1 /*
2  * Copyright 2005 Joe Walker
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.directwebremoting.impl;
17
18 import java.lang.reflect.InvocationTargetException JavaDoc;
19 import java.lang.reflect.Method JavaDoc;
20 import java.lang.reflect.Modifier JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.Collections JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.Map JavaDoc;
26
27 import org.directwebremoting.AjaxFilter;
28 import org.directwebremoting.AjaxFilterChain;
29 import org.directwebremoting.WebContext;
30 import org.directwebremoting.WebContextFactory;
31 import org.directwebremoting.extend.AccessControl;
32 import org.directwebremoting.extend.AjaxFilterManager;
33 import org.directwebremoting.extend.Call;
34 import org.directwebremoting.extend.Calls;
35 import org.directwebremoting.extend.Converter;
36 import org.directwebremoting.extend.ConverterManager;
37 import org.directwebremoting.extend.Creator;
38 import org.directwebremoting.extend.CreatorManager;
39 import org.directwebremoting.extend.NamedConverter;
40 import org.directwebremoting.extend.Property;
41 import org.directwebremoting.extend.EnginePrivate;
42 import org.directwebremoting.extend.Remoter;
43 import org.directwebremoting.extend.Replies;
44 import org.directwebremoting.extend.Reply;
45 import org.directwebremoting.util.Continuation;
46 import org.directwebremoting.util.JavascriptUtil;
47 import org.directwebremoting.util.LocalUtil;
48 import org.directwebremoting.util.Logger;
49
50 /**
51  * In implementation of Remoter that delegates requests to a set of Modules
52  * @author Joe Walker [joe at getahead dot ltd dot uk]
53  * @author Mike Wilson
54  */

55 public class DefaultRemoter implements Remoter
56 {
57     /* (non-Javadoc)
58      * @see org.directwebremoting.Remoter#generateInterfaceScript(java.lang.String, java.lang.String)
59      */

60     public String JavaDoc generateInterfaceScript(String JavaDoc scriptName, String JavaDoc path) throws SecurityException JavaDoc
61     {
62         String JavaDoc actualPath = path;
63         if (overridePath != null)
64         {
65             actualPath = overridePath;
66         }
67
68         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
69
70         // Output the class definitions for the converted objects
71
Collection JavaDoc converterMatches = converterManager.getConverterMatchStrings();
72         Iterator JavaDoc it = converterMatches.iterator();
73         while (it.hasNext())
74         {
75             String JavaDoc match = (String JavaDoc) it.next();
76
77             try
78             {
79                 StringBuffer JavaDoc paramBuffer = new StringBuffer JavaDoc();
80
81                 Converter conv = converterManager.getConverterByMatchString(match);
82                 // We will only generate JavaScript classes for compound objects/beans
83
if (conv instanceof NamedConverter)
84                 {
85                     NamedConverter boConv = (NamedConverter) conv;
86                     String JavaDoc jsClassName = boConv.getJavascript();
87
88                     // We need a configured JavaScript class name
89
if (jsClassName != null && !jsClassName.equals(""))
90                     {
91                         // Wildcard match strings are currently not supported
92
if (match.indexOf("*") == -1)
93                         {
94                             paramBuffer.append('\n');
95
96                             // output: if (typeof <class> != "function") { var <class> = function() {
97
paramBuffer.append("if (typeof " + jsClassName + " != \"function\") {\n");
98                             paramBuffer.append(" var " + jsClassName + " = function() {\n");
99
100                             // output: this.<property> = <init-value>;
101
Class JavaDoc mappedType;
102                             try
103                             {
104                                 mappedType = LocalUtil.classForName(match);
105                             }
106                             catch (ClassNotFoundException JavaDoc ex)
107                             {
108                                 throw new IllegalArgumentException JavaDoc(ex.getMessage());
109                             }
110
111                             Map JavaDoc properties = boConv.getPropertyMapFromClass(mappedType, true, true);
112                             for (Iterator JavaDoc pit = properties.entrySet().iterator(); pit.hasNext();)
113                             {
114                                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) pit.next();
115                                 String JavaDoc name = (String JavaDoc) entry.getKey();
116                                 Property property = (Property) entry.getValue();
117                                 Class JavaDoc propType = property.getPropertyType();
118
119                                 // Property name
120
paramBuffer.append(" this." + name + " = ");
121
122                                 // Default property values
123
if (propType.isArray())
124                                 {
125                                     paramBuffer.append("[]");
126                                 }
127                                 else if (propType == boolean.class)
128                                 {
129                                     paramBuffer.append("false");
130                                 }
131                                 else if (propType.isPrimitive())
132                                 {
133                                     paramBuffer.append("0");
134                                 }
135                                 else
136                                 {
137                                     paramBuffer.append("null");
138                                 }
139
140                                 paramBuffer.append(";\n");
141                             }
142
143                             paramBuffer.append(" }\n");
144                             paramBuffer.append("}\n");
145                         }
146                     }
147                 }
148
149                 buffer.append(paramBuffer.toString());
150             }
151             catch (Exception JavaDoc ex)
152             {
153                 log.warn("Failed to create parameter declaration for " + match, ex);
154                 buffer.append("// Missing parameter declaration for " + match + ". See the server logs for details.");
155             }
156         }
157
158         Creator creator = creatorManager.getCreator(scriptName);
159
160         buffer.append('\n');
161
162         String JavaDoc init = EnginePrivate.getEngineInitScript();
163         buffer.append(init);
164
165         buffer.append("if (" + scriptName + " == null) var " + scriptName + " = {};\n");
166         buffer.append(scriptName + "._path = '" + actualPath + "';\n");
167
168         Method JavaDoc[] methods = creator.getType().getMethods();
169         for (int i = 0; i < methods.length; i++)
170         {
171             Method JavaDoc method = methods[i];
172             String JavaDoc methodName = method.getName();
173
174             // We don't need to check accessControl.getReasonToNotExecute()
175
// because the checks are made by the execute() method, but we do
176
// check if we can display it
177
try
178             {
179                 accessControl.assertIsDisplayable(creator, scriptName, method);
180             }
181             catch (SecurityException JavaDoc ex)
182             {
183                 if (!allowImpossibleTests)
184                 {
185                     continue;
186                 }
187             }
188
189             // Is it on the list of banned names
190
if (JavascriptUtil.isReservedWord(methodName))
191             {
192                 continue;
193             }
194
195             // Check to see if the creator is reloadable
196
// If it is, then do not cache the generated Javascript
197
String JavaDoc script;
198             if (!creator.isCacheable())
199             {
200                 script = getMethodJS(scriptName, method);
201             }
202             else
203             {
204                 String JavaDoc key = scriptName + "." + method.getName();
205
206                 // For optimal performance we might use the Memoizer pattern
207
// JCiP#108 however performance isn't a big issue and we are
208
// prepared to cope with getMethodJS() being run more than once.
209
script = (String JavaDoc) methodCache.get(key);
210                 if (script == null)
211                 {
212                     script = getMethodJS(scriptName, method);
213                     methodCache.put(key, script);
214                 }
215             }
216
217             buffer.append(script);
218         }
219
220         return buffer.toString();
221     }
222
223     /**
224      * Generates Javascript for a given Java method
225      * @param scriptName Name of the Javascript file, sans ".js" suffix
226      * @param method Target method
227      * @return Javascript implementing the DWR call for the target method
228      */

229     private String JavaDoc getMethodJS(String JavaDoc scriptName, Method JavaDoc method)
230     {
231         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
232
233         String JavaDoc methodName = method.getName();
234         buffer.append(scriptName + '.' + methodName + " = function(");
235         Class JavaDoc[] paramTypes = method.getParameterTypes();
236         for (int j = 0; j < paramTypes.length; j++)
237         {
238             if (!LocalUtil.isServletClass(paramTypes[j]))
239             {
240                 buffer.append("p" + j + ", ");
241             }
242         }
243
244         buffer.append("callback) {\n");
245
246         String JavaDoc executeFunctionName = EnginePrivate.getExecuteFunctionName();
247         buffer.append(" " + executeFunctionName + "(" + scriptName + "._path, '" + scriptName + "', '" + methodName + "\', ");
248         for (int j = 0; j < paramTypes.length; j++)
249         {
250             if (LocalUtil.isServletClass(paramTypes[j]))
251             {
252                 buffer.append("false, ");
253             }
254             else
255             {
256                 buffer.append("p" + j + ", ");
257             }
258         }
259
260         buffer.append("callback);\n");
261         buffer.append("}\n");
262
263         return buffer.toString();
264     }
265
266     /* (non-Javadoc)
267      * @see org.directwebremoting.Remoter#execute(org.directwebremoting.Calls)
268      */

269     public Replies execute(Calls calls)
270     {
271         Replies replies = new Replies(calls.getBatchId());
272
273         int callCount = calls.getCallCount();
274         if (callCount > maxCallCount)
275         {
276             log.error("Call count for batch exceeds maxCallCount. Add an init-param of maxCallCount to increase this limit");
277             throw new SecurityException JavaDoc("Call count for batch is too high");
278         }
279
280         for (int callNum = 0; callNum < callCount; callNum++)
281         {
282             Call call = calls.getCall(callNum);
283             Reply reply = execute(call);
284             replies.addReply(reply);
285         }
286
287         return replies;
288     }
289
290     /**
291      * Execute a single call object
292      * @param call The call to execute
293      * @return A Reply to the Call
294      */

295     public Reply execute(Call call)
296     {
297         try
298         {
299             Method JavaDoc method = call.getMethod();
300             if (method == null || call.getException() != null)
301             {
302                 return new Reply(call.getCallId(), null, call.getException());
303             }
304
305             // Get a list of the available matching methods with the coerced
306
// parameters that we will use to call it if we choose to use that
307
// method.
308
Creator creator = creatorManager.getCreator(call.getScriptName());
309
310             // We don't need to check accessControl.getReasonToNotExecute()
311
// because the checks are made by the doExec method, but we do check
312
// if we can display it
313
accessControl.assertExecutionIsPossible(creator, call.getScriptName(), method);
314
315             // Get ourselves an object to execute a method on unless the
316
// method is static
317
Object JavaDoc object = null;
318             String JavaDoc scope = creator.getScope();
319             boolean create = false;
320
321             if (!Modifier.isStatic(method.getModifiers()))
322             {
323                 WebContext webcx = WebContextFactory.get();
324
325                 // Check the various scopes to see if it is there
326
if (scope.equals(Creator.APPLICATION))
327                 {
328                     object = webcx.getServletContext().getAttribute(call.getScriptName());
329                 }
330                 else if (scope.equals(Creator.SESSION))
331                 {
332                     object = webcx.getSession().getAttribute(call.getScriptName());
333                 }
334                 else if (scope.equals(Creator.SCRIPT))
335                 {
336                     object = webcx.getScriptSession().getAttribute(call.getScriptName());
337                 }
338                 else if (scope.equals(Creator.REQUEST))
339                 {
340                     object = webcx.getHttpServletRequest().getAttribute(call.getScriptName());
341                 }
342                 // Creator.PAGE scope means we create one every time anyway
343

344                 // If we don't have an object the call the creator
345
if (object == null)
346                 {
347                     create = true;
348                     object = creator.getInstance();
349                 }
350
351                 // Remember it for next time
352
if (create)
353                 {
354                     if (scope.equals(Creator.APPLICATION))
355                     {
356                         // This might also be done at application startup by
357
// DefaultCreatorManager.addCreator(String, Creator)
358
webcx.getServletContext().setAttribute(call.getScriptName(), object);
359                     }
360                     else if (scope.equals(Creator.SESSION))
361                     {
362                         webcx.getSession().setAttribute(call.getScriptName(), object);
363                     }
364                     else if (scope.equals(Creator.SCRIPT))
365                     {
366                         webcx.getScriptSession().setAttribute(call.getScriptName(), object);
367                     }
368                     else if (scope.equals(Creator.REQUEST))
369                     {
370                         webcx.getHttpServletRequest().setAttribute(call.getScriptName(), object);
371                     }
372                     // Creator.PAGE scope means we create one every time anyway
373
}
374             }
375
376             // Some debug
377
log.info("Exec: " + call.getScriptName() + "." + call.getMethodName() + "()");
378             if (log.isDebugEnabled())
379             {
380                 StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
381
382                 if (create)
383                 {
384                     buffer.append("--Object created, ");
385                     if (!scope.equals(Creator.PAGE))
386                     {
387                         buffer.append(" stored in ");
388                         buffer.append(scope);
389                     }
390                     else
391                     {
392                         buffer.append(" not stored");
393                     }
394                 }
395                 else
396                 {
397                     buffer.append("--Object found in ");
398                     buffer.append(scope);
399                 }
400                 buffer.append(". ");
401
402                 // It would be good to debug the params but it's not easy
403
//buffer.append("Call params (");
404
//for (int j = 0; j < inctx.getParameterCount(callNum); j++)
405
//{
406
// if (j != 0)
407
// {
408
// buffer.append(", ");
409
// }
410
// InboundVariable param = inctx.getParameter(callNum, j);
411
// buffer.append(param.toString());
412
//}
413
//buffer.append(") ");
414

415                 buffer.append("id=");
416                 buffer.append(call.getCallId());
417
418                 log.debug(buffer.toString());
419             }
420
421             // Execute the filter chain method.toString()
422
final Iterator JavaDoc it = ajaxFilterManager.getAjaxFilters(call.getScriptName());
423             AjaxFilterChain chain = new AjaxFilterChain()
424             {
425                 public Object JavaDoc doFilter(Object JavaDoc obj, Method JavaDoc meth, Object JavaDoc[] p) throws Exception JavaDoc
426                 {
427                     AjaxFilter next = (AjaxFilter) it.next();
428                     return next.doFilter(obj, meth, p, this);
429                 }
430             };
431             Object JavaDoc reply = chain.doFilter(object, method, call.getParameters());
432             return new Reply(call.getCallId(), reply);
433         }
434         catch (InvocationTargetException JavaDoc ex)
435         {
436             // Allow Jetty RequestRetry exception to propogate to container
437
Continuation.rethrowIfContinuation(ex);
438
439             log.warn("Method execution failed: ", ex.getTargetException());
440             return new Reply(call.getCallId(), null, ex.getTargetException());
441         }
442         catch (Exception JavaDoc ex)
443         {
444             // Allow Jetty RequestRetry exception to propogate to container
445
Continuation.rethrowIfContinuation(ex);
446
447             log.warn("Method execution failed: ", ex);
448             return new Reply(call.getCallId(), null, ex);
449         }
450     }
451
452     /**
453      * Accessor for the CreatorManager that we configure
454      * @param creatorManager The new ConverterManager
455      */

456     public void setCreatorManager(CreatorManager creatorManager)
457     {
458         this.creatorManager = creatorManager;
459     }
460
461     /**
462      * Accessor for the ConverterManager that we configure
463      * @param converterManager The new ConverterManager
464      */

465     public void setConverterManager(ConverterManager converterManager)
466     {
467         this.converterManager = converterManager;
468     }
469
470     /**
471      * Accessor for the security manager
472      * @param accessControl The accessControl to set.
473      */

474     public void setAccessControl(AccessControl accessControl)
475     {
476         this.accessControl = accessControl;
477     }
478
479     /**
480      * Accessor for the AjaxFilterManager
481      * @param ajaxFilterManager The AjaxFilterManager to set.
482      */

483     public void setAjaxFilterManager(AjaxFilterManager ajaxFilterManager)
484     {
485         this.ajaxFilterManager = ajaxFilterManager;
486     }
487
488     /**
489      * If we need to override the default path
490      * @param overridePath The new override path
491      */

492     public void setOverridePath(String JavaDoc overridePath)
493     {
494         this.overridePath = overridePath;
495     }
496
497     /**
498      * Do we allow impossible tests for debug purposes
499      * @param allowImpossibleTests The allowImpossibleTests to set.
500      */

501     public void setAllowImpossibleTests(boolean allowImpossibleTests)
502     {
503         this.allowImpossibleTests = allowImpossibleTests;
504     }
505
506     /**
507      * To prevent a DoS attack we limit the max number of calls that can be
508      * made in a batch
509      * @param maxCallCount the maxCallCount to set
510      */

511     public void setMaxCallCount(int maxCallCount)
512     {
513         this.maxCallCount = maxCallCount;
514     }
515
516     /**
517      * What AjaxFilters apply to which Ajax calls?
518      */

519     private AjaxFilterManager ajaxFilterManager = null;
520
521     /**
522      * How we create new beans
523      */

524     protected CreatorManager creatorManager = null;
525
526     /**
527      * How we convert beans - or in this case create client side classes
528      */

529     protected ConverterManager converterManager = null;
530
531     /**
532      * The security manager
533      */

534     protected AccessControl accessControl = null;
535
536     /**
537      * If we need to override the default path
538      */

539     private String JavaDoc overridePath = null;
540
541     /**
542      * This helps us test that access rules are being followed
543      */

544     private boolean allowImpossibleTests = false;
545
546     /**
547      * To prevent a DoS attack we limit the max number of calls that can be
548      * made in a batch
549      */

550     private int maxCallCount = 20;
551
552     /**
553      * Generated Javascript cache
554      */

555     private Map JavaDoc methodCache = Collections.synchronizedMap(new HashMap JavaDoc());
556
557     /**
558      * The log stream
559      */

560     private static final Logger log = Logger.getLogger(DefaultRemoter.class);
561 }
562
Popular Tags