KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > core > utility > ControllerRun


1 /* ====================================================================
2  * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3  *
4  * Copyright (c) 1995-2002 Jcorporate Ltd. 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 following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution,
19  * if any, must include the following acknowledgment:
20  * "This product includes software developed by Jcorporate Ltd.
21  * (http://www.jcorporate.com/)."
22  * Alternately, this acknowledgment may appear in the software itself,
23  * if and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. "Jcorporate" and product names such as "Expresso" must
26  * not be used to endorse or promote products derived from this
27  * software without prior written permission. For written permission,
28  * please contact info@jcorporate.com.
29  *
30  * 5. Products derived from this software may not be called "Expresso",
31  * or other Jcorporate product names; nor may "Expresso" or other
32  * Jcorporate product names appear in their name, without prior
33  * written permission of Jcorporate Ltd.
34  *
35  * 6. No product derived from this software may compete in the same
36  * market space, i.e. framework, without prior written permission
37  * of Jcorporate Ltd. For written permission, please contact
38  * partners@jcorporate.com.
39  *
40  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This software consists of voluntary contributions made by many
55  * individuals on behalf of the Jcorporate Ltd. Contributions back
56  * to the project(s) are encouraged when you make modifications.
57  * Please send them to support@jcorporate.com. For more information
58  * on Jcorporate Ltd. and its products, please see
59  * <http://www.jcorporate.com/>.
60  *
61  * Portions of this software are based upon other open source
62  * products and are subject to their respective licenses.
63  */

64
65 /*
66  * ControllerRun.java
67  *
68  * Copyright 1999, 2000, 2001 Jcorporate Ltd.
69  */

70 package com.jcorporate.expresso.core.utility;
71
72 import com.jcorporate.expresso.core.controller.Controller;
73 import com.jcorporate.expresso.core.controller.ControllerElement;
74 import com.jcorporate.expresso.core.controller.ControllerException;
75 import com.jcorporate.expresso.core.controller.ControllerRequest;
76 import com.jcorporate.expresso.core.controller.ControllerResponse;
77 import com.jcorporate.expresso.core.controller.DBController;
78 import com.jcorporate.expresso.core.controller.Input;
79 import com.jcorporate.expresso.core.controller.NonHandleableException;
80 import com.jcorporate.expresso.core.controller.Output;
81 import com.jcorporate.expresso.core.controller.State;
82 import com.jcorporate.expresso.core.controller.Transition;
83 import com.jcorporate.expresso.core.controller.session.SimplePersistentSession;
84 import com.jcorporate.expresso.core.dbobj.ValidValue;
85 import com.jcorporate.expresso.core.misc.ConfigManager;
86 import com.jcorporate.expresso.core.misc.StringUtil;
87 import com.jcorporate.expresso.core.registry.MutableRequestRegistry;
88 import com.jcorporate.expresso.core.security.SuperUser;
89 import com.jcorporate.expresso.kernel.LogManager;
90 import com.jcorporate.expresso.services.html.Button;
91 import com.jcorporate.expresso.services.html.Cell;
92 import com.jcorporate.expresso.services.html.CheckBox;
93 import com.jcorporate.expresso.services.html.DropDown;
94 import com.jcorporate.expresso.services.html.Form;
95 import com.jcorporate.expresso.services.html.HiddenField;
96 import com.jcorporate.expresso.services.html.HtmlException;
97 import com.jcorporate.expresso.services.html.Row;
98 import com.jcorporate.expresso.services.html.Table;
99 import com.jcorporate.expresso.services.html.Text;
100 import com.jcorporate.expresso.services.html.TextField;
101
102 import java.io.BufferedReader JavaDoc;
103 import java.io.File JavaDoc;
104 import java.io.IOException JavaDoc;
105 import java.io.InputStreamReader JavaDoc;
106 import java.util.Enumeration JavaDoc;
107 import java.util.Hashtable JavaDoc;
108 import java.util.StringTokenizer JavaDoc;
109 import java.util.Vector JavaDoc;
110
111
112 /**
113  * Special servlet designed to interact with server-side Controller objects
114  *
115  * @author Michael Nash
116  */

117 public class ControllerRun {
118     protected static final String JavaDoc thisClass = ControllerRun.class.getName();
119     private static SimplePersistentSession mySession = new SimplePersistentSession();
120
121     /**
122      * @param v
123      * @param t
124      */

125     private void add(Vector JavaDoc v, Table t)
126             throws HtmlException {
127         for (Enumeration JavaDoc e = v.elements(); e.hasMoreElements();) {
128             ControllerElement o = (ControllerElement) e.nextElement();
129
130             if (o instanceof Output) {
131                 addOutput((Output) o, t);
132             } else if (o instanceof Input) {
133                 addInput((Input) o, t);
134             }
135         } /* for each element in the vector */
136
137     } /* add(Vector, Table) */
138
139
140     /**
141      * @param h
142      * @param r
143      * @param f
144      */

145     private void addActions(Vector JavaDoc h, Row r, Form f)
146             throws HtmlException {
147         Transition a = null;
148         Cell oneCell = null;
149         Button oneButton = null;
150         StringBuffer JavaDoc paramString = null;
151         String JavaDoc paramName = null;
152         Hashtable JavaDoc params = null;
153         HiddenField hidden = null;
154
155         for (Enumeration JavaDoc e = h.elements(); e.hasMoreElements();) {
156             a = (Transition) e.nextElement();
157             params = a.getParams();
158             paramString = new StringBuffer JavaDoc();
159
160             for (Enumeration JavaDoc p = params.keys(); p.hasMoreElements();) {
161                 paramName = (String JavaDoc) p.nextElement();
162                 paramString.append("&");
163                 paramString.append(paramName);
164                 paramString.append("=");
165                 paramString.append((String JavaDoc) params.get(paramName));
166             }
167
168             hidden = new HiddenField(a.getName() + "_params",
169                     a.getControllerObject() +
170                     paramString.toString());
171             f.add(hidden);
172         }
173         for (Enumeration JavaDoc e = h.elements(); e.hasMoreElements();) {
174             a = (Transition) e.nextElement();
175             oneCell = new Cell();
176             oneButton = new Button("button_" + a.getName(), a.getLabel());
177             oneCell.add(oneButton);
178             r.add(oneCell);
179         }
180     } /* addActions(Vector, Row, Form) */
181
182
183     /**
184      * @param i
185      * @param t
186      */

187     private void addInput(Input i, Table t)
188             throws HtmlException {
189         Row oneRow = new Row();
190         t.add(oneRow);
191
192         Cell oneCell = null;
193         String JavaDoc l = i.getLabel();
194
195         if (l != null) {
196             oneCell = new Cell();
197             oneCell.add(new Text(l, "bold"));
198             oneRow.add(oneCell);
199         }
200
201         oneCell = new Cell();
202
203         String JavaDoc defaultValue = i.getDefaultValue();
204
205         if (defaultValue == null) {
206             defaultValue = ("");
207         }
208
209         /* if the input is multi-valued, use a drop-down list */
210         Vector JavaDoc validValues = i.getValidValues();
211         ValidValue oneValue = null;
212
213         if (validValues != null) {
214             DropDown d = new DropDown(i.getName());
215
216             for (Enumeration JavaDoc vv = validValues.elements();
217                  vv.hasMoreElements();) {
218                 oneValue = (ValidValue) vv.nextElement();
219                 d.addOption(oneValue.getValue(), oneValue.getDescription());
220             }
221
222             d.setCurrentValue(defaultValue);
223             oneCell.add(d);
224         } else if (i.getType().equalsIgnoreCase("boolean")) {
225
226             /* if it's boolean, use a checkbox */
227             if (defaultValue.equalsIgnoreCase("Y")) {
228                 oneCell.add(new CheckBox(i.getName(), "Y", true));
229             } else {
230                 oneCell.add(new CheckBox(i.getName(), "Y", false));
231             }
232         } else {
233
234             /* else use a text field */
235             oneCell.add(new TextField(i.getName(), defaultValue,
236                     i.getMaxLength(), i.getDisplayLength()));
237         }
238
239         oneRow.add(oneCell);
240
241         if (i.getNested() != null) {
242             Vector JavaDoc v = i.getNested();
243
244             for (Enumeration JavaDoc ve = v.elements(); ve.hasMoreElements();) {
245                 addInput((Input) ve.nextElement(), t);
246             }
247         }
248     } /* addInput(Input, Table) */
249
250
251     /**
252      * Add the designated output to the given table
253      *
254      * @param o The Output object to be added to this table
255      * @param t The Table object that the output should be added to
256      */

257     private void addOutput(Output o, Table t)
258             throws HtmlException {
259         Row oneRow = new Row();
260         t.add(oneRow);
261
262         Cell oneCell = null;
263         String JavaDoc l = o.getLabel();
264
265         if (l == null) {
266             l = ("");
267         }
268         if (!l.equals("")) {
269             oneCell = new Cell();
270
271             if (o.getStyle() != null) {
272                 oneCell.add(new Text(l, o.getStyle()));
273             } else {
274                 oneCell.add(new Text(l));
275             }
276
277             oneRow.add(oneCell);
278         }
279
280         String JavaDoc c = o.getContent();
281
282         if (c != null) {
283             oneCell = new Cell();
284             oneCell.add(new Text(c));
285             oneRow.add(oneCell);
286         }
287         if (o.getNested() != null) {
288             Vector JavaDoc v = o.getNested();
289
290             if (v.size() > 0) {
291                 Table t2 = new Table("Nested Output");
292                 t2.setBorder(1);
293
294                 Row r = new Row();
295                 t.add(r);
296
297                 Cell nestedCell = new Cell();
298                 nestedCell.add(t2);
299                 r.add(nestedCell);
300
301                 for (Enumeration JavaDoc ve = v.elements(); ve.hasMoreElements();) {
302                     addOutput((Output) ve.nextElement(), t2);
303                 }
304             } /* if nested vector has contents */
305
306         } /* if any nested elements */
307
308     } /* addOutput(Output, Table) */
309
310
311     /**
312      * @param a
313      */

314     private static void displayTransition(Transition a) {
315         System.out.println(" " + a.getName());
316     } /* displayTransition(Action) */
317
318     /**
319      * @param i
320      */

321     private static void displayInput(Input i) {
322         System.out.println(" " + i.getName());
323
324         ValidValue oneValue = null;
325         Vector JavaDoc v = i.getValidValues();
326
327         if (v != null) {
328             for (Enumeration JavaDoc e = v.elements(); e.hasMoreElements();) {
329                 oneValue = (ValidValue) e.nextElement();
330                 System.out.println(" " + oneValue.getValue() + "/" +
331                         oneValue.getDescription());
332             }
333         }
334     } /* displayInput(Input) */
335
336     /**
337      * @param o
338      * @param nestLevel
339      */

340     private static void displayOutput(Output o, int nestLevel) {
341         Output nested = null;
342
343         for (int i = 0; i <= nestLevel; i++) {
344             System.out.print(" ");
345         }
346
347         System.out.println(o.getName() + ":" + o.getContent());
348
349         for (Enumeration JavaDoc e = o.getNested().elements(); e.hasMoreElements();) {
350             nested = (Output) e.nextElement();
351             displayOutput(nested, nestLevel + 1);
352         }
353     } /* displayOutput(Output, int) */
354
355     /**
356      * Perform the named Controller
357      *
358      * @param conName
359      */

360     public static void doController(String JavaDoc conName, Hashtable JavaDoc commandArgs)
361             throws IOException JavaDoc, ControllerException,
362             HtmlException, NonHandleableException {
363         Controller con = ConfigManager.getControllerFactory().getController(conName);
364         ControllerRequest params = new ControllerRequest();
365         params.setUid(1);
366         params.setSession(mySession);
367
368         Hashtable JavaDoc states = con.getStates();
369         String JavaDoc thisStateName = null;
370         State oneState = null;
371         String JavaDoc command = ("");
372         String JavaDoc newState = null;
373
374         /* Pass any other arguments to the controller directly */
375         String JavaDoc oneKey = null;
376         String JavaDoc oneValue = null;
377
378         for (Enumeration JavaDoc eg = commandArgs.keys(); eg.hasMoreElements();) {
379             oneKey = (String JavaDoc) eg.nextElement();
380             oneValue = (String JavaDoc) commandArgs.get(oneKey);
381
382             if (oneValue != null) {
383                 if ((!oneKey.equals("webAppDir")) &&
384                         (!oneKey.equals("configDir")) &&
385                         (!oneKey.equals(Controller.STATE_PARAM_KEY))) {
386                     params.setParameter(oneKey, oneValue);
387                 }
388             } /* if value not null */
389
390         } /* for */
391
392         if (con instanceof DBController) {
393             String JavaDoc dbName = StringUtil.notNull((String JavaDoc) commandArgs.get("db"));
394             params.setDataContext(dbName);
395         }
396
397         boolean interactiveMode = true;
398         String JavaDoc initialState = (String JavaDoc) commandArgs.get(Controller.STATE_PARAM_KEY);
399
400         /* If state is specified, we run in non-interactive mode */
401         if (initialState == null) {
402             initialState = con.getInitialState();
403         } else {
404             interactiveMode = false;
405         }
406         if (initialState != null) {
407             newState(con, params, initialState);
408         }
409
410         Hashtable JavaDoc commands = new Hashtable JavaDoc(3);
411
412         while ((!command.equals("0")) && interactiveMode) {
413             int i = 0;
414             System.out.println("---------------------------------");
415             System.out.println("0. Exit Controller");
416
417             for (Enumeration JavaDoc hs = states.keys(); hs.hasMoreElements();) {
418                 i++;
419                 thisStateName = (String JavaDoc) hs.nextElement();
420                 commands.put(("" + i), thisStateName);
421                 oneState = (State) states.get(thisStateName);
422                 System.out.println(i + ". State:" + thisStateName +
423                         ", Description:" +
424                         oneState.getDescription());
425
426                 String JavaDoc param = null;
427                 Vector JavaDoc p = oneState.getParameters();
428
429                 if (p.size() > 0) {
430                     System.out.print("\tParameters required:");
431                 }
432                 for (Enumeration JavaDoc ea = p.elements(); ea.hasMoreElements();) {
433                     param = (String JavaDoc) ea.nextElement();
434                     System.out.print(" " + param);
435                 }
436                 if (p.size() > 0) {
437                     System.out.println("");
438                 }
439             } /* for each state */
440
441
442             System.out.print("Enter state, or 0 to quit:");
443
444             BufferedReader JavaDoc br = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(System.in));
445             command = br.readLine();
446
447             if (command.equals("0")) {
448                 System.exit(0);
449             }
450
451             newState = (String JavaDoc) commands.get(command);
452
453             if (newState == null) {
454                 System.out.println("\nInvalid entry\n");
455             } else {
456                 newState(con, params, newState);
457             }
458         } /* while command not "exit" */
459
460
461         System.exit(0);
462     } /* doController(String) */
463
464
465     /**
466      * Main method so that ControllerRun can be launched from a command line
467      *
468      * @param args[] Command line arguments to supply the information to
469      * connect to the database
470      */

471     public static void main(String JavaDoc[] args) {
472         System.out.println("ControllerRun");
473
474         Hashtable JavaDoc commandArgs = new Hashtable JavaDoc(10);
475         String JavaDoc paramCode = null;
476         String JavaDoc paramValue = null;
477
478         for (int i = 0; i < args.length; i++) {
479             StringTokenizer JavaDoc stk = new StringTokenizer JavaDoc(args[i], "=");
480
481             if (!stk.hasMoreTokens()) {
482                 usage();
483             }
484
485             paramCode = stk.nextToken();
486
487             if (!stk.hasMoreTokens()) {
488                 usage();
489             }
490
491             paramValue = stk.nextToken();
492             commandArgs.put(paramCode, paramValue);
493         } /* for each command-line argument */
494
495         try {
496
497             String JavaDoc logDir = (String JavaDoc) commandArgs.get("logDir");
498             String JavaDoc logConfig = (String JavaDoc) commandArgs.get("logConfig");
499             if (logConfig == null) {
500                 logConfig = (String JavaDoc) commandArgs.get("configDir") + "/expressoLogging.xml";
501             }
502
503             LogManager lm = new LogManager(logConfig, logDir);
504
505             // set up ConfigManager first
506
ConfigManager.setWebAppDir(getAppDir(commandArgs));
507
508             // set up ConfigManager first
509
ConfigManager.load(getConfigDir(commandArgs));
510
511             //initialize the db pool
512
ConfigManager.dbInitialize();
513
514             //call the LoadLists method and load up the lists used by this application
515
String JavaDoc dbName = (String JavaDoc) commandArgs.get("db");
516             new MutableRequestRegistry(dbName,
517                     SuperUser.SUPER_USER);
518
519             String JavaDoc conName = (String JavaDoc) commandArgs.get("controller");
520             System.out.println("Running Controller " + conName);
521             doController(conName, commandArgs);
522         } catch (Exception JavaDoc de) {
523             System.out.println("ControllerRun Error:" + de.getMessage());
524             de.printStackTrace();
525             System.exit(1);
526         }
527     } /* main(String]) */
528
529
530     private static String JavaDoc getConfigDir(Hashtable JavaDoc commandArgs)
531             throws IOException JavaDoc {
532         String JavaDoc configDir = StringUtil.notNull((String JavaDoc) commandArgs.get("configDir"));
533
534         if (configDir.equals("")) {
535             boolean noDir = true;
536             System.out.println("No configDir=xxx command line argument was found.");
537
538             while (noDir) {
539                 System.out.print("Please enter Expresso configuration directory:");
540
541                 BufferedReader JavaDoc br = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(System.in));
542                 configDir = br.readLine();
543
544                 if (StringUtil.notNull(configDir).equals("")) {
545                     System.out.println("You must enter a directory name, not blank");
546                     continue;
547                 }
548
549                 File JavaDoc theDir = new File JavaDoc(configDir);
550
551                 if (!theDir.isDirectory()) {
552                     System.out.println(configDir +
553                             " is not a valid directory. ");
554                     continue;
555                 } else {
556                     noDir = false;
557                 }
558             }
559         }
560
561         return configDir;
562     } /* getConfigDir(Hashtable) */
563
564
565     private static String JavaDoc getAppDir(Hashtable JavaDoc commandArgs)
566             throws IOException JavaDoc {
567         String JavaDoc webAppDir = StringUtil.notNull((String JavaDoc) commandArgs.get("webAppDir"));
568
569         if (webAppDir.equals("")) {
570             boolean noDir = true;
571             System.out.println("No webAppDir=xxx command line argument was found.");
572
573             while (noDir) {
574                 System.out.print("Please enter Expresso web application root directory:");
575
576                 BufferedReader JavaDoc br = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(System.in));
577                 webAppDir = br.readLine();
578
579                 if (StringUtil.notNull(webAppDir).equals("")) {
580                     System.out.println("You must enter a directory name, not blank");
581                     continue;
582                 }
583
584                 File JavaDoc theDir = new File JavaDoc(webAppDir);
585
586                 if (!theDir.isDirectory()) {
587                     System.out.println(webAppDir +
588                             " is not a valid directory. ");
589                     continue;
590                 } else {
591                     noDir = false;
592                 }
593             }
594         }
595
596         return webAppDir;
597     } /* getWebAppDir(Hashtable) */
598
599
600     /**
601      * @param con
602      * @param newState
603      */

604     private static void newState(Controller con, ControllerRequest cparams,
605                                  String JavaDoc newState)
606             throws ControllerException, IOException JavaDoc,
607             HtmlException, NonHandleableException {
608         String JavaDoc myName = ("com.jcorporate.expresso.core.utility." +
609                 "ControllerRun.newState(Controller, String)");
610         State thisState = con.getState(newState);
611
612         /* prompt for the parameters required by this state */
613         String JavaDoc param = null;
614         String JavaDoc newValue = null;
615         BufferedReader JavaDoc br = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(System.in));
616
617         for (Enumeration JavaDoc ea = thisState.getParameters().elements();
618              ea.hasMoreElements();) {
619             param = (String JavaDoc) ea.nextElement();
620
621             if (StringUtil.notNull(cparams.getParameter(param)).equals("")) {
622                 System.out.print("Value for " + param + ":");
623                 newValue = br.readLine();
624                 cparams.setParameter(param, newValue);
625             }
626         }
627
628         System.out.println("Transitioning to state " + newState + "...");
629
630         ControllerResponse myResponse = con.newState(newState, cparams);
631
632         if (myResponse.isCustomResponse()) {
633             throw new ControllerException("Controller returned a 'custom' " +
634                     "response to the client. This controller/state cannot be " +
635                     "run in this environment.");
636         }
637
638         /* Else build a page from the info provided by the Controller */
639         Vector JavaDoc inputs = myResponse.getInputs();
640
641         if (inputs == null) {
642             inputs = new Vector JavaDoc(1);
643         }
644
645         Vector JavaDoc outputs = myResponse.getOutputs();
646
647         if (outputs == null) {
648             outputs = new Vector JavaDoc(1);
649         }
650
651         Vector JavaDoc actions = myResponse.getTransitions();
652
653         if (actions == null) {
654             actions = new Vector JavaDoc(1);
655         }
656         if ((inputs.size() == 0) && (outputs.size() == 0) &&
657                 (actions.size() == 0)) {
658             throw new ControllerException(myName + ":No input, output " +
659                     "or actions defined for this Controller in this state");
660         }
661         if (outputs.size() != 0) {
662             System.out.println(outputs.size() + " Outputs:");
663
664             for (Enumeration JavaDoc oe = outputs.elements(); oe.hasMoreElements();) {
665                 displayOutput((Output) oe.nextElement(), 0);
666             }
667         } else { /* if outputs not null */
668             System.out.println("No outputs");
669         }
670         if (inputs.size() != 0) {
671             System.out.println(inputs.size() + " Inputs:");
672
673             for (Enumeration JavaDoc ie = inputs.elements(); ie.hasMoreElements();) {
674                 displayInput((Input) ie.nextElement());
675             }
676         } else { /* if inputs not null */
677             System.out.println("No Inputs");
678         }
679         if (actions.size() != 0) {
680             System.out.println(actions.size() + " Actions:");
681
682             for (Enumeration JavaDoc ae = actions.elements(); ae.hasMoreElements();) {
683                 displayTransition((Transition) ae.nextElement());
684             }
685         } else { /* if actions not null */
686             System.out.println("No actions");
687         }
688
689         //if (f.getContentCount() > 0) {
690
//myPage.add(f);
691
//}
692
} /* newState(Controller, String) */
693
694
695     /**
696      *
697      */

698     private static void usage() {
699         System.out.println("Usage:");
700         System.exit(1);
701     } /* usage() */
702
703 } /* ControllerRun */
704
Popular Tags