KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jpublish > action > DefaultActionManager


1 /*--
2
3  Copyright (C) 2001-2003 Aetrion LLC.
4  All rights reserved.
5  
6  Redistribution and use in source and binary forms, with or without
7  modification, are permitted provided that the following conditions
8  are met:
9  
10  1. Redistributions of source code must retain the above copyright
11     notice, this list of conditions, and the following disclaimer.
12  
13  2. Redistributions in binary form must reproduce the above copyright
14     notice, this list of conditions, and the disclaimer that follows
15     these conditions in the documentation and/or other materials
16     provided with the distribution.
17
18  3. The name "JPublish" must not be used to endorse or promote products
19     derived from this software without prior written permission. For
20     written permission, please contact info@aetrion.com.
21  
22  4. Products derived from this software may not be called "JPublish", nor
23     may "JPublish" appear in their name, without prior written permission
24     from Aetrion LLC (info@aetrion.com).
25  
26  In addition, the authors of this software request (but do not require)
27  that you include in the end-user documentation provided with the
28  redistribution and/or in the software itself an acknowledgement equivalent
29  to the following:
30      "This product includes software developed by
31       Aetrion LLC (http://www.aetrion.com/)."
32
33  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
34  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36  DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
37  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
42  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
43  POSSIBILITY OF SUCH DAMAGE.
44
45  For more information on JPublish, please see <http://www.jpublish.org/>.
46  
47  */

48
49 package org.jpublish.action;
50
51 import java.io.IOException JavaDoc;
52 import java.util.*;
53
54 import com.anthonyeden.lib.config.Configuration;
55 import com.anthonyeden.lib.config.ConfigurationException;
56 import com.anthonyeden.lib.util.ClassUtilities;
57 import com.anthonyeden.lib.util.MessageUtilities;
58 import org.apache.bsf.BSFManager;
59 import org.apache.commons.logging.Log;
60 import org.apache.commons.logging.LogFactory;
61 import org.apache.commons.vfs.FileContent;
62 import org.apache.commons.vfs.FileName;
63 import org.apache.commons.vfs.FileObject;
64 import org.apache.commons.vfs.FileSystemManager;
65 import org.jpublish.*;
66 import org.jpublish.action.bsf.BSFScriptHandler;
67 import org.jpublish.cache.CacheEntry;
68 import org.jpublish.cache.CacheStorage;
69 import org.jpublish.cache.HashMapCacheStorage;
70 import org.jpublish.module.Module;
71 import org.jpublish.util.PathUtilities;
72
73 /**
74  * Default implementation of the ActionManager interface.
75  *
76  * @author Anthony Eden
77  */

78
79 public class DefaultActionManager extends ManagerBase implements ActionManager {
80
81     /**
82      * Default path used if a path, pre-evaluation or post-evaluation action does not
83      * include a path attribute. The default value is "*".
84      */

85     public static final String JavaDoc DEFAULT_PATH = "*";
86     
87     private static final String JavaDoc ATTRIBUTE_NAME = "name";
88     private static final String JavaDoc ATTRIBUTE_PATH = "path";
89     private static final String JavaDoc ATTRIBUTE_CLASSNAME = "classname";
90     private static final String JavaDoc ATTRIBUTE_NAMESPACE = "namespace";
91     private static final String JavaDoc ATTRIBUTE_EXECUTE_RESULT = "execute-result";
92     private static final String JavaDoc ATTRIBUTE_EXTENSION = "extension";
93
94     private static Log log = LogFactory.getLog(DefaultActionManager.class);
95
96     private Map scriptHandlers = new HashMap();
97     private Map definedActions = new HashMap();
98     private List startupActions = new ArrayList();
99     private List shutdownActions = new ArrayList();
100     private List globalActions = new ArrayList();
101     private List pathActions = new ArrayList();
102     private List preEvaluationActions = new ArrayList();
103     private List postEvaluationActions = new ArrayList();
104
105     private CacheStorage scriptActionCache = null;
106     private ScriptHandlerDefinition defaultHandlerDefinition = null;
107
108     /**
109      * Construct a new DefaultActionManager.
110      */

111
112     public DefaultActionManager() {
113         log.debug("Configuring BSF");
114         // configure BSF
115
configureBSF();
116     }
117
118     /**
119      * Get the script action cache. The default cache is a simple HashMap cache.
120      *
121      * @return The script action cache
122      */

123
124     public synchronized CacheStorage getScriptActionCache() {
125         if (scriptActionCache == null) {
126             scriptActionCache = new HashMapCacheStorage();
127         }
128         return scriptActionCache;
129     }
130
131     /**
132      * Set the script action cache. This method can be used to override the default caching mechanism; for example a
133      * LRU cache or distributed cache could be used.
134      *
135      * @param scriptActionCache The new script action cache
136      */

137
138     public void setScriptActionCache(CacheStorage scriptActionCache) {
139         this.scriptActionCache = scriptActionCache;
140     }
141
142     /**
143      * Return the virtual file system manager. This method may return null if the implementation does not support a
144      * virtual file system.
145      *
146      * @return The FileSystemManager
147      */

148
149     public synchronized FileSystemManager getFileSystemManager()
150             throws IOException JavaDoc {
151         if (fileSystemManager == null) {
152             configureDefaultFileSystemManager(SiteContext.DEFAULT_ACTION_ROOT);
153         }
154         return fileSystemManager;
155     }
156
157     /**
158      * Get a Map of all defined actions.
159      *
160      * @return A Map of defined actions
161      */

162
163     public Map getDefinedActions() {
164         return definedActions;
165     }
166
167     /**
168      * Get a List of startup actions.
169      *
170      * @return List of startup actions
171      */

172
173     public List getStartupActions() {
174         return startupActions;
175     }
176
177     /**
178      * Get a List of shutdown actions.
179      *
180      * @return List of shutdown actions
181      */

182
183     public List getShutdownActions() {
184         return shutdownActions;
185     }
186
187     /**
188      * Get a List of global actions.
189      *
190      * @return List of global actions
191      */

192
193     public List getGlobalActions() {
194         return globalActions;
195     }
196
197     /**
198      * Get the List of path actions.
199      *
200      * @return The path actions
201      */

202
203     public List getPathActions() {
204         return pathActions;
205     }
206
207     /**
208      * Get the List of actions which are executed immediately upon receipt of any request. These actions are executed
209      * before a page search occurs.
210      *
211      * @return The pre-evaluation actions
212      */

213
214     public List getPreEvaluationActions() {
215         return preEvaluationActions;
216     }
217
218     /**
219      * Get the List of actions which are executed after the HTTP request has been completed, but before the response is
220      * sent back to the client.
221      *
222      * @return The post-evaluation actions
223      */

224
225     public List getPostEvaluationActions() {
226         return postEvaluationActions;
227     }
228
229     /**
230      * Execute all startup actions.
231      */

232
233     public void executeStartupActions() {
234         log.debug("Executing startup actions");
235         Iterator actions = getStartupActions().iterator();
236         while (actions.hasNext()) {
237             ActionWrapper action = (ActionWrapper) actions.next();
238             action.execute(null);
239         }
240     }
241
242     /**
243      * Execute all shutdown actions.
244      */

245
246     public void executeShutdownActions() {
247         log.debug("Executing shutdown actions");
248         Iterator actions = getShutdownActions().iterator();
249         while (actions.hasNext()) {
250             ActionWrapper action = (ActionWrapper) actions.next();
251             action.execute(null);
252         }
253     }
254
255     /**
256      * Execute all global actions using the given context.
257      *
258      * @param context The current context
259      */

260
261     public void executeGlobalActions(RequestContext context) {
262         Iterator actions = globalActions.iterator();
263         while (actions.hasNext()) {
264             ActionWrapper action = (ActionWrapper) actions.next();
265             action.execute(context);
266         }
267     }
268
269     /**
270      * Execute the path actions with the given context.
271      *
272      * @param path The request path
273      * @param context The current context
274      */

275
276     public void executePathActions(String JavaDoc path, RequestContext context) {
277         Iterator actions = getPathActions().iterator();
278         while (actions.hasNext()) {
279             ActionWrapper actionWrapper = (ActionWrapper) actions.next();
280             PathAction action = (PathAction) actionWrapper.getAction();
281             if (PathUtilities.match(path, action.getPath())) {
282                 actionWrapper.execute(context);
283             }
284         }
285     }
286
287     /**
288      * Execute pre-evaluation actions. Pre-evaluation actions are only executed if their path argument matches the
289      * current path.
290      *
291      * <p><b>Note:</b> Since these actions are executed prior to locating the page configuration the page variable is
292      * not in the context.
293      *
294      * <p>To stop processing and return immediately, set the value <code>stop-processing</code> in the context to a
295      * non-null value.
296      *
297      * @param path The request path
298      * @param context The current request context
299      * @return True if the processing should stop
300      */

301
302     public boolean executePreEvaluationActions(String JavaDoc path,
303             RequestContext context) {
304         Iterator actions = getPreEvaluationActions().iterator();
305         while (actions.hasNext()) {
306             ActionWrapper actionWrapper = (ActionWrapper) actions.next();
307             PathAction action = (PathAction) actionWrapper.getAction();
308             if (PathUtilities.match(path, action.getPath())) {
309                 actionWrapper.execute(context);
310                 if (context.getStopProcessing() != null) {
311                     return true;
312                 }
313             }
314         }
315         return false;
316     }
317
318     /**
319      * Execute post-evaluation actions.
320      *
321      * @param path The request path
322      * @param context The request contex
323      */

324
325     public void executePostEvaluationActions(String JavaDoc path,
326             RequestContext context) {
327         Iterator actions = getPostEvaluationActions().iterator();
328         while (actions.hasNext()) {
329             ActionWrapper actionWrapper = (ActionWrapper) actions.next();
330             PathAction action = (PathAction) actionWrapper.getAction();
331             if (PathUtilities.match(path, action.getPath())) {
332                 actionWrapper.execute(context);
333             }
334         }
335     }
336
337     /**
338      * Execute the named action with the current context.
339      *
340      * @param name The action name
341      */

342     public void execute(String JavaDoc name) {
343         RequestContext context = RequestContext.getRequestContext();
344         execute(name, context);
345     }
346
347     /**
348      * Execute the named action with the given context.
349      *
350      * @param name The action name
351      * @param context The current context
352      */

353
354     public void execute(String JavaDoc name, RequestContext context) {
355         if (log.isDebugEnabled()) {
356             log.debug("Executing action: " + name);
357         }
358
359         Action action = findAction(name);
360         if (action != null) {
361             action.execute(context, null);
362         }
363     }
364
365     /**
366      * Find an action with the given name. The name may be the name of an action registered with the ActionManager at
367      * startup, an action from a module, a partial file path rooted in the action root directory or a fully qualified
368      * Java class name.
369      *
370      * @param name The name of the action
371      * @return The action
372      * @throws ActionNotFoundException If the action is not found
373      */

374
375     public Action findAction(String JavaDoc name) {
376
377         // look in registered classes first
378
log.debug("Looking for registered action.");
379         Action action = (Action) definedActions.get(name);
380         if (action != null) {
381             log.debug("Registered action found.");
382             return action;
383         }
384         
385         // look in modules
386
log.debug("Looking for action in modules.");
387         Iterator modules = siteContext.getModules().iterator();
388         while (modules.hasNext()) {
389             Module module = (Module) modules.next();
390             action = (Action) (module.getDefinedActions().get(name));
391             if (action != null) {
392                 log.debug("Action found in module.");
393                 return action;
394             }
395         }
396         
397         // look in scripts
398
Action scriptAction = findScriptAction(name);
399         if (scriptAction != null) {
400             return scriptAction;
401         }
402         
403         // look in classpath
404
try {
405             log.debug("Looking for action in the classpath.");
406             action = (Action) ClassUtilities.loadClass(name).newInstance();
407             return action;
408         } catch (ClassNotFoundException JavaDoc e) {
409             if (log.isDebugEnabled()) {
410                 log.debug("Action not found in classpath");
411             }
412         } catch (Exception JavaDoc e) {
413             Object JavaDoc[] args = {name, e.getMessage()};
414             throw new ActionNotFoundException(MessageUtilities.getMessage(getClass(), JPublishEngine.MESSAGE_PACKAGE, "actionNotFound",
415                     args), e, name);
416         }
417
418         log.error("Action not found: " + name);
419
420         Object JavaDoc[] args = {name};
421         throw new ActionNotFoundException(MessageUtilities.getMessage(getClass(), JPublishEngine.MESSAGE_PACKAGE, "actionNotFound",
422                 args), name);
423
424     }
425
426     /**
427      * Look for script actions.
428      *
429      * @param name The name of the action
430      * @return The Action or null
431      */

432
433     private Action findScriptAction(String JavaDoc name) {
434         CacheStorage scriptActionCache = getScriptActionCache();
435         CacheEntry cacheEntry = (CacheEntry) scriptActionCache.get(name);
436         Action action = null;
437         try {
438             log.debug("Looking for action in scripts.");
439             FileSystemManager fileSystemManager = getFileSystemManager();
440             FileObject baseFile = fileSystemManager.getBaseFile();
441             FileObject file = fileSystemManager.resolveFile(baseFile,
442                     PathUtilities.toRelativePath(name));
443             FileName fileName = file.getName();
444             FileContent content = file.getContent();
445             String JavaDoc extension = fileName.getExtension();
446
447             ScriptHandlerDefinition scriptHandlerDefinition =
448                     (ScriptHandlerDefinition) scriptHandlers.get(extension);
449             if (scriptHandlerDefinition == null) {
450                 scriptHandlerDefinition = defaultHandlerDefinition;
451             }
452
453             if (cacheEntry == null) {
454                 log.debug("Action not in cache");
455                 if (file.exists()) {
456                     if (log.isDebugEnabled()) {
457                         log.debug("Script action found [" + file + "]");
458                     }
459                     action = new ScriptAction(siteContext, file,
460                             scriptHandlerDefinition);
461                     scriptActionCache.put(name, new CacheEntry(action,
462                             content.getLastModifiedTime()));
463                 }
464             } else {
465                 log.debug("Action found in cache");
466                 action = (Action) cacheEntry.getObject();
467                 if (cacheEntry.getLastModified() !=
468                         content.getLastModifiedTime()) {
469                     if (log.isDebugEnabled()) {
470                         log.debug("Reloading action [" + file + "]");
471                     }
472
473                     action = new ScriptAction(siteContext, file,
474                             scriptHandlerDefinition);
475                     scriptActionCache.put(name, new CacheEntry(action,
476                             content.getLastModifiedTime()));
477                 }
478             }
479         } catch (IOException JavaDoc e) {
480             log.error("IO exception: " + e);
481             if (log.isDebugEnabled()) {
482                 e.printStackTrace();
483             }
484         } catch (Exception JavaDoc e) {
485             log.error("Exception: " + e);
486             Object JavaDoc[] args = {name, e.getMessage()};
487             throw new JPublishRuntimeException(MessageUtilities.getMessage(getClass(), JPublishEngine.MESSAGE_PACKAGE,
488                     "errorLoadingScript", args), e);
489         }
490
491         return action;
492     }
493
494     /**
495      * Load the ActionManager's configuration from the given configuration object.
496      *
497      * @param configuration The configuration object
498      * @throws ConfigurationException
499      */

500
501     public void loadConfiguration(Configuration configuration) throws
502             ConfigurationException {
503         super.loadConfiguration(configuration);
504
505         try {
506             // set the cache storage type
507
Configuration cacheStorageElement = configuration.getChild("cache-storage");
508             if (cacheStorageElement != null) {
509                 String JavaDoc className = cacheStorageElement.getAttribute(ATTRIBUTE_CLASSNAME,
510                         HashMapCacheStorage.class.getName());
511                 CacheStorage cache = (CacheStorage) ClassUtilities.loadClass(className).newInstance();
512                 cache.loadConfiguration(cacheStorageElement);
513                 setScriptActionCache(cache);
514             }
515             
516             // load action definitions
517
Iterator defineActionElements = configuration.getChildren("define-action").iterator();
518             while (defineActionElements.hasNext()) {
519                 Configuration defineActionElement =
520                         (Configuration) defineActionElements.next();
521                 String JavaDoc name = defineActionElement.getAttribute(ATTRIBUTE_NAME);
522                 String JavaDoc className =
523                         defineActionElement.getAttribute(ATTRIBUTE_CLASSNAME);
524                 Action action = (Action) ClassUtilities.loadClass(className).newInstance();
525                 if (log.isDebugEnabled()) {
526                     log.debug("Defined action: " + name + " [" +
527                             className + "]");
528                 }
529                 definedActions.put(name, action);
530             }
531             
532             // load XWork action definitions
533
Iterator defineXWorkActionElements = configuration.getChildren("define-xwork-action").iterator();
534             while (defineXWorkActionElements.hasNext()) {
535                 Configuration defineXWorkActionElement =
536                         (Configuration) defineXWorkActionElements.next();
537                 String JavaDoc name = defineXWorkActionElement.getAttribute(ATTRIBUTE_NAME);
538                 String JavaDoc namespace = defineXWorkActionElement.getAttribute(ATTRIBUTE_NAMESPACE);
539                 String JavaDoc executeResult = defineXWorkActionElement.getAttribute(ATTRIBUTE_EXECUTE_RESULT);
540
541                 XWorkAction action = new XWorkAction();
542
543                 action.setName(name);
544                 action.setNamespace(namespace);
545                 action.setExecuteResult(executeResult);
546
547                 if (log.isDebugEnabled()) {
548                     log.debug("Defined XWork action: " + name + " [ns=" +
549                             namespace + "]");
550                 }
551                 definedActions.put(name, action);
552             }
553             
554             // load script handlers
555
log.debug("Loading script handlers");
556             Iterator scriptHandlerElements =
557                     configuration.getChildren("script-handler").iterator();
558             while (scriptHandlerElements.hasNext()) {
559                 Configuration scriptHandlerElement =
560                         (Configuration) scriptHandlerElements.next();
561                 String JavaDoc className = scriptHandlerElement.getAttribute(ATTRIBUTE_CLASSNAME);
562                 String JavaDoc extension = scriptHandlerElement.getAttribute(ATTRIBUTE_EXTENSION);
563                 ScriptHandlerDefinition scriptHandlerDefinition =
564                         new ScriptHandlerDefinition();
565                 scriptHandlerDefinition.setHandlerClass(ClassUtilities.loadClass(className));
566                 scriptHandlerDefinition.loadConfiguration(scriptHandlerElement);
567
568                 if (log.isDebugEnabled()) {
569                     log.debug("Script handler for " + extension + ": " +
570                             className);
571                 }
572
573                 if (extension == null) {
574                     defaultHandlerDefinition = scriptHandlerDefinition;
575                 } else {
576                     scriptHandlers.put(extension, scriptHandlerDefinition);
577                 }
578             }
579             
580             // load startup actions
581
Iterator startupActionElements =
582                     configuration.getChildren("startup-action").iterator();
583             while (startupActionElements.hasNext()) {
584                 Configuration startupActionElement =
585                         (Configuration) startupActionElements.next();
586                 String JavaDoc name = startupActionElement.getAttribute(ATTRIBUTE_NAME);
587                 startupActions.add(new ActionWrapper(findAction(name),
588                         startupActionElement));
589             }
590             
591             // load shutdown actions
592
Iterator shutdownActionElements =
593                     configuration.getChildren("shutdown-action").iterator();
594             while (shutdownActionElements.hasNext()) {
595                 Configuration shutdownActionElement =
596                         (Configuration) shutdownActionElements.next();
597                 String JavaDoc name = shutdownActionElement.getAttribute(ATTRIBUTE_NAME);
598                 shutdownActions.add(new ActionWrapper(findAction(name),
599                         shutdownActionElement));
600             }
601             
602             // load global actions
603
log.debug("Configuring global actions");
604             Iterator globalActionElements =
605                     configuration.getChildren("global-action").iterator();
606             while (globalActionElements.hasNext()) {
607                 Configuration globalActionElement =
608                         (Configuration) globalActionElements.next();
609                 String JavaDoc name = globalActionElement.getAttribute(ATTRIBUTE_NAME);
610                 if (log.isDebugEnabled()) {
611                     log.debug("Finding global action '" + name + "'");
612                 }
613                 Action action = findAction(name);
614                 if (action == null) {
615                     log.error("No action '" + name + "' found");
616                 } else {
617                     if (log.isDebugEnabled()) {
618                         log.debug("Action '" + name + "' found");
619                     }
620                     globalActions.add(new ActionWrapper(action,
621                             globalActionElement));
622                 }
623             }
624             
625             // load path actions
626
Iterator pathActionElements =
627                     configuration.getChildren("path-action").iterator();
628             while (pathActionElements.hasNext()) {
629                 Configuration pathActionElement =
630                         (Configuration) pathActionElements.next();
631                 String JavaDoc name = pathActionElement.getAttribute(ATTRIBUTE_NAME);
632                 String JavaDoc path = pathActionElement.getAttribute(ATTRIBUTE_PATH);
633                 if (path == null) {
634                     path = DEFAULT_PATH;
635                 }
636
637                 pathActions.add(new ActionWrapper(new PathAction(path, findAction(name)), pathActionElement));
638             }
639             
640             // load pre-evaluation actions
641
Iterator preEvaluationActionElements =
642                     configuration.getChildren("pre-evaluation-action").iterator();
643             while (preEvaluationActionElements.hasNext()) {
644                 Configuration preEvaluationActionElement =
645                         (Configuration) preEvaluationActionElements.next();
646                 String JavaDoc name = preEvaluationActionElement.getAttribute(ATTRIBUTE_NAME);
647                 String JavaDoc path = preEvaluationActionElement.getAttribute(ATTRIBUTE_PATH);
648                 if (path == null) {
649                     path = DEFAULT_PATH;
650                 }
651
652                 preEvaluationActions.add(new ActionWrapper(new PathAction(path, findAction(name)),
653                         preEvaluationActionElement));
654             }
655             
656             // load post-evaluation actions
657
Iterator postEvaluationActionElements =
658                     configuration.getChildren("post-evaluation-action").iterator();
659             while (postEvaluationActionElements.hasNext()) {
660                 Configuration postEvaluationActionElement =
661                         (Configuration) postEvaluationActionElements.next();
662                 String JavaDoc name = postEvaluationActionElement.getAttribute(ATTRIBUTE_NAME);
663                 String JavaDoc path = postEvaluationActionElement.getAttribute(ATTRIBUTE_PATH);
664                 if (path == null) {
665                     path = DEFAULT_PATH;
666                 }
667
668                 postEvaluationActions.add(new ActionWrapper(new PathAction(path, findAction(name)),
669                         postEvaluationActionElement));
670             }
671         } catch (Exception JavaDoc e) {
672             Object JavaDoc[] args = {e.getMessage()};
673             throw new ConfigurationException(MessageUtilities.getMessage(getClass(), JPublishEngine.MESSAGE_PACKAGE,
674                     "errorLoadingConfiguration", args), e);
675         }
676     }
677
678     /**
679      * Configure the BSF engine for supported languages which are not pre-defined in the BSF engine's configuration.
680      */

681
682     private void configureBSF() {
683         log.debug("Adding Beanshell to BSF");
684         String JavaDoc[] beanshellExtensions = {"bsh"};
685         BSFManager.registerScriptingEngine("beanshell", "bsh.util.BeanShellBSFEngine", beanshellExtensions);
686
687         log.debug("Adding Groovy to BSF");
688         String JavaDoc[] groovyExtensions = {"groovy", "gy"};
689         BSFManager.registerScriptingEngine("groovy", "org.codehaus.groovy.bsf.GroovyEngine", groovyExtensions);
690
691         defaultHandlerDefinition = new ScriptHandlerDefinition();
692         defaultHandlerDefinition.setHandlerClass(BSFScriptHandler.class);
693
694     }
695
696 }
697
Popular Tags