1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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) {
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
310
311
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
443 if (controller == null && controllerType == null) {
444 return null;
445 }
446
447
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) {
488 try {
489 return createControllerFromClassname(name);
490
491 } catch (InstantiationException ex) {
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 }