View Javadoc

1   /*
2    * $Id: ComponentDefinition.java 421151 2006-07-12 06:07:14Z wsmoak $
3    *
4    * Copyright 1999-2004 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * 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, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.struts.tiles;
20  
21  import java.io.Serializable;
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.struts.tiles.xmlDefinition.XmlDefinition;
28  import org.apache.struts.util.RequestUtils;
29  
30  /***
31   * Definition of a template / component attributes.
32   * Attributes of a component can be defined with the help of this class.
33   * An instance of this class can be used as a bean, and passed to 'insert' tag.
34   */
35  public class ComponentDefinition implements Serializable {
36  
37      /***
38       * Commons Logging instance.
39       */
40      protected static Log log = LogFactory.getLog(ComponentDefinition.class);
41  
42      /***
43       * Definition name
44       */
45      protected String name = null;
46  
47      /***
48       * Component / template path (URL).
49       */
50      protected String path = null;
51  
52      /***
53       * Attributes defined for the component.
54       */
55      protected Map attributes = null;
56  
57      /***
58       * Role associated to definition.
59       */
60      protected String role = null;
61  
62      /*** Associated Controller URL or classname, if defined */
63      protected String controller = null;
64  
65      /***
66       * Associated Controller typename, if controllerName defined.
67       * Can be CONTROLLER, ACTION or URL, or null.
68       */
69      protected String controllerType = null;
70  
71      /***
72       * Controller name type.
73       */
74      public static final String URL = "url";
75  
76      /***
77       * Controller name type.
78       */
79      public static final String CONTROLLER = "controller";
80  
81      /***
82       * Controller name type.
83       */
84      public static final String ACTION = "action";
85  
86      /***
87       * Controller associated to Definition.
88       * Lazy creation : only on first request
89       */
90      private Controller controllerInstance = null;
91  
92      /***
93       * Constructor.
94       */
95      public ComponentDefinition() {
96          attributes = new HashMap();
97      }
98  
99      /***
100      * Copy Constructor.
101      * Create a new definition initialized with parent definition.
102      * Do a shallow copy : attributes are shared between copies, but not the Map
103      * containing attributes.
104      */
105     public ComponentDefinition(ComponentDefinition definition) {
106         attributes = new HashMap(definition.getAttributes());
107         this.name = definition.getName();
108         this.path = definition.getPath();
109         this.role = definition.getRole();
110         this.controllerInstance = definition.getControllerInstance();
111         this.controller = definition.getController();
112         this.controllerType = definition.getControllerType();
113     }
114 
115     /***
116      * Constructor.
117      * Create a new definition initialized from a RawDefinition.
118      * Raw definitions are used to read definition from a data source (xml file, db, ...).
119      * A RawDefinition mainly contains properties of type String, while Definition
120      * contains more complex type (ex : Controller).
121      * Do a shallow copy : attributes are shared between objects, but not the Map
122      * containing attributes.
123      * OO Design issues : Actually RawDefinition (XmlDefinition) extends ComponentDefinition.
124      * This must not be the case. I have do it because I am lazy.
125      * @throws InstantiationException if an error occur while instanciating Controller :
126      * (classname can't be instanciated, Illegal access with instanciated class,
127      * Error while instanciating class, classname can't be instanciated.
128      */
129     public ComponentDefinition(XmlDefinition definition) {
130 
131         this((ComponentDefinition) definition);
132     }
133 
134     /***
135      * Constructor.
136      */
137     public ComponentDefinition(String name, String path, Map attributes) {
138         this.name = name;
139         this.path = path;
140         this.attributes = attributes;
141     }
142 
143     /***
144      * Access method for the name property.
145      *
146      * @return   the current value of the name property
147      */
148     public String getName() {
149         return name;
150     }
151 
152     /***
153      * Sets the value of the name property.
154      *
155      * @param aName the new value of the name property
156      */
157     public void setName(String aName) {
158         name = aName;
159     }
160 
161     /***
162      * Access method for the path property.
163      *
164      * @return The current value of the path property.
165      */
166     public String getPage() {
167         return path;
168     }
169 
170     /***
171      * Sets the value of the path property.
172      *
173      * @param page the new value of the path property
174      */
175     public void setPage(String page) {
176         path = page;
177     }
178 
179     /***
180      * Access method for the path property.
181      *
182      * @return   the current value of the path property
183      */
184     public String getPath() {
185         return path;
186     }
187 
188     /***
189      * Sets the value of the path property.
190      *
191      * @param aPath the new value of the path property
192      */
193     public void setPath(String aPath) {
194         path = aPath;
195     }
196 
197     /***
198      * Access method for the template property.
199      * Same as getPath()
200      * @return   the current value of the template property
201      */
202     public String getTemplate() {
203         return path;
204     }
205 
206     /***
207      * Sets the value of the template property.
208      * Same as setPath()
209      *
210      * @param template the new value of the path property
211      */
212     public void setTemplate(String template) {
213         path = template;
214     }
215 
216     /***
217      * Access method for the role property.
218      * @return   the current value of the role property
219      */
220     public String getRole() {
221         return role;
222     }
223 
224     /***
225      * Sets the value of the role property.
226      *
227      * @param role the new value of the path property
228      */
229     public void setRole(String role) {
230         this.role = role;
231     }
232 
233     /***
234      * Access method for the attributes property.
235      * If there is no attributes, return an empty map.
236      * @return   the current value of the attributes property
237      */
238     public Map getAttributes() {
239         return attributes;
240     }
241 
242     /***
243      * Returns the value of the named attribute as an Object, or null if no
244      * attribute of the given name exists.
245      *
246      * @return   requested attribute or null if not found
247      */
248     public Object getAttribute(String key) {
249         return attributes.get(key);
250     }
251 
252     /***
253      * Put a new attribute in this component
254      *
255      * @param key String key for attribute
256      * @param value Attibute value.
257      */
258     public void putAttribute(String key, Object value) {
259         attributes.put(key, value);
260     }
261 
262     /***
263      * Put an attribute in component / template definition.
264      * Attribute can be used as content for tag get.
265      * @param name Attribute name
266      * @param content Attribute value
267      */
268     public void put(String name, Object content) {
269         put(name, content, false, null);
270     }
271 
272     /***
273      * Put an attribute in template definition.
274      * Attribute can be used as content for tag get.
275      * @param name Attribute name
276      * @param content Attribute value �
277      * @param direct Determines how content is handled by get tag: true means content is printed directly; false, the default, means content is included
278      */
279     public void put(String name, Object content, boolean direct) {
280         put(name, content, direct, null);
281     }
282 
283     /***
284      * Put an attribute in template definition.
285      * Attribute can be used as content for tag get.
286      * @param name Attribute name
287      * @param content Attribute value
288      * @param direct Determines how content is handled by get tag: true means content is printed directly; false, the default, means content is included
289      * @param role Determine if content is used by get tag. If user is in role, content is used.
290      */
291     public void put(String name, Object content, boolean direct, String role) {
292         if (direct == true) { // direct String
293             put(name, content, "string", role);
294         } else {
295             put(name, content, "template", role);
296         }
297 
298     }
299 
300     /***
301      * Put an attribute in template definition.
302      * Attribute can be used as content for tag get.
303      * @param name Attribute name
304      * @param content Attribute value
305      * @param type attribute type: template, string, definition
306      * @param role Determine if content is used by get tag. If user is in role, content is used.
307      */
308     public void put(String name, Object content, String type, String role) {
309         // Is there a type set ?
310         // First check direct attribute, and translate it to a valueType.
311         // Then, evaluate valueType, and create requested typed attribute.
312         AttributeDefinition attribute = null;
313 
314         if (content != null
315             && type != null
316             && !(content instanceof AttributeDefinition)) {
317 
318             String strValue = content.toString();
319             if (type.equalsIgnoreCase("string")) {
320                 attribute = new DirectStringAttribute(strValue);
321 
322             } else if (type.equalsIgnoreCase("page")) {
323                 attribute = new PathAttribute(strValue);
324 
325             } else if (type.equalsIgnoreCase("template")) {
326                 attribute = new PathAttribute(strValue);
327 
328             } else if (type.equalsIgnoreCase("instance")) {
329                 attribute = new DefinitionNameAttribute(strValue);
330 
331             } else if (type.equalsIgnoreCase("definition")) {
332                 attribute = new DefinitionNameAttribute(strValue);
333             }
334         }
335 
336         putAttribute(name, attribute);
337     }
338 
339     /***
340      * Returns a description of the attributes.
341      */
342     public String toString() {
343         return "{name="
344             + name
345             + ", path="
346             + path
347             + ", role="
348             + role
349             + ", controller="
350             + controller
351             + ", controllerType="
352             + controllerType
353             + ", controllerInstance="
354             + controllerInstance
355             + ", attributes="
356             + attributes
357             + "}\n";
358     }
359 
360     /***
361      * Get associated controller type.
362      * Type denote a fully qualified classname.
363      */
364     public String getControllerType() {
365         return controllerType;
366     }
367 
368     /***
369      * Set associated controller type.
370      * Type denote a fully qualified classname.
371      * @param controllerType Typeof associated controller
372      */
373     public void setControllerType(String controllerType) {
374         this.controllerType = controllerType;
375     }
376 
377     /***
378      * Set associated controller name as an url, and controller
379      * type as "url".
380      * Name must be an url (not checked).
381      * Convenience method.
382      * @param controller Controller url
383      */
384     public void setControllerUrl(String controller) {
385         setController(controller);
386         setControllerType("url");
387     }
388 
389     /***
390      * Set associated controller name as a classtype, and controller
391      * type as "classname".
392      * Name denote a fully qualified classname
393      * Convenience method.
394      * @param controller Controller classname.
395      */
396     public void setControllerClass(String controller) {
397         setController(controller);
398         setControllerType("classname");
399     }
400 
401     /***
402      * Get associated controller local URL.
403      * URL should be local to webcontainer in order to allow request context followup.
404      * URL comes as a string.
405      */
406     public String getController() {
407         return controller;
408     }
409 
410     /***
411      * Set associated controller URL.
412      * URL should be local to webcontainer in order to allow request context followup.
413      * URL is specified as a string.
414      * @param url Url called locally
415      */
416     public void setController(String url) {
417         this.controller = url;
418     }
419 
420     /***
421      * Get controller instance.
422      * @return controller instance.
423      */
424     public Controller getControllerInstance() {
425         return controllerInstance;
426     }
427 
428     /***
429      * Get or create controller.
430      * Get controller, create it if necessary.
431      * @return controller if controller or controllerType is set, null otherwise.
432      * @throws InstantiationException if an error occur while instanciating Controller :
433      * (classname can't be instanciated, Illegal access with instanciated class,
434      * Error while instanciating class, classname can't be instanciated.
435      */
436     public Controller getOrCreateController() throws InstantiationException {
437 
438         if (controllerInstance != null) {
439             return controllerInstance;
440         }
441 
442         // Do we define a controller ?
443         if (controller == null && controllerType == null) {
444             return null;
445         }
446 
447         // check parameters
448         if (controllerType != null && controller == null) {
449             throw new InstantiationException("Controller name should be defined if controllerType is set");
450         }
451 
452         controllerInstance = createController(controller, controllerType);
453 
454         return controllerInstance;
455     }
456 
457     /***
458      * Set controller.
459      */
460     public void setControllerInstance(Controller controller) {
461         this.controllerInstance = controller;
462     }
463 
464     /***
465      * Create a new instance of controller named in parameter.
466      * If controllerType is specified, create controller accordingly.
467      * Otherwise, if name denote a classname, create an instance of it. If class is
468      *  subclass of org.apache.struts.action.Action, wrap controller
469      * appropriately.
470      * Otherwise, consider name as an url.
471      * @param name Controller name (classname, url, ...)
472      * @param controllerType Expected Controller type
473      * @return org.apache.struts.tiles.Controller
474      * @throws InstantiationException if an error occur while instanciating Controller :
475      * (classname can't be instanciated, Illegal access with instanciated class,
476      * Error while instanciating class, classname can't be instanciated.
477      */
478     public static Controller createController(String name, String controllerType)
479         throws InstantiationException {
480 
481         if (log.isDebugEnabled()) {
482             log.debug("Create controller name=" + name + ", type=" + controllerType);
483         }
484 
485         Controller controller = null;
486 
487         if (controllerType == null) { // first try as a classname
488             try {
489                 return createControllerFromClassname(name);
490 
491             } catch (InstantiationException ex) { // ok, try something else
492                 controller = new UrlController(name);
493             }
494 
495         } else if ("url".equalsIgnoreCase(controllerType)) {
496             controller = new UrlController(name);
497 
498         } else if ("classname".equalsIgnoreCase(controllerType)) {
499             controller = createControllerFromClassname(name);
500         }
501 
502         return controller;
503     }
504 
505     /***
506      * Create a controller from specified classname
507      * @param classname Controller classname.
508      * @return org.apache.struts.tiles.Controller
509      * @throws InstantiationException if an error occur while instanciating Controller :
510      * (classname can't be instanciated, Illegal access with instanciated class,
511      * Error while instanciating class, classname can't be instanciated.
512      */
513     public static Controller createControllerFromClassname(String classname)
514         throws InstantiationException {
515 
516         try {
517             Class requestedClass = RequestUtils.applicationClass(classname);
518             Object instance = requestedClass.newInstance();
519 
520             if (log.isDebugEnabled()) {
521                 log.debug("Controller created : " + instance);
522             }
523             return (Controller) instance;
524 
525         } catch (java.lang.ClassNotFoundException ex) {
526             throw new InstantiationException(
527                 "Error - Class not found :" + ex.getMessage());
528 
529         } catch (java.lang.IllegalAccessException ex) {
530             throw new InstantiationException(
531                 "Error - Illegal class access :" + ex.getMessage());
532 
533         } catch (java.lang.InstantiationException ex) {
534             throw ex;
535 
536         } catch (java.lang.ClassCastException ex) {
537             throw new InstantiationException(
538                 "Controller of class '"
539                     + classname
540                     + "' should implements 'Controller' or extends 'Action'");
541         }
542     }
543 
544 }