View Javadoc

1   /*
2    * $Id: InsertTag.java 167834 2005-05-03 00:14:12Z germuska $ 
3    *
4    * Copyright 1999-2005 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.taglib;
20  
21  import java.io.IOException;
22  import java.io.PrintWriter;
23  import java.util.Map;
24  import java.util.StringTokenizer;
25  
26  import javax.servlet.ServletException;
27  import javax.servlet.http.HttpServletRequest;
28  import javax.servlet.http.HttpServletResponse;
29  import javax.servlet.jsp.JspException;
30  import javax.servlet.jsp.PageContext;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.struts.Globals;
35  import org.apache.struts.tiles.taglib.util.TagUtils;
36  import org.apache.struts.tiles.AttributeDefinition;
37  import org.apache.struts.tiles.ComponentContext;
38  import org.apache.struts.tiles.ComponentDefinition;
39  import org.apache.struts.tiles.Controller;
40  import org.apache.struts.tiles.DefinitionAttribute;
41  import org.apache.struts.tiles.DefinitionNameAttribute;
42  import org.apache.struts.tiles.DefinitionsFactoryException;
43  import org.apache.struts.tiles.DirectStringAttribute;
44  import org.apache.struts.tiles.FactoryNotFoundException;
45  import org.apache.struts.tiles.NoSuchDefinitionException;
46  import org.apache.struts.tiles.TilesUtil;
47  
48  /***
49   * This is the tag handler for <tiles:insert>, which includes
50   * a template. The tag's body content consists of <tiles:put>
51   * tags, which are accessed by <tiles:get> in the template.
52   *
53   * @version $Rev: 167834 $ $Date: 2005-05-02 17:14:12 -0700 (Mon, 02 May 2005) $
54   */
55  public class InsertTag
56  	extends DefinitionTagSupport
57  	implements PutTagParent, ComponentConstants, PutListTagParent {
58  
59  	/*** 
60  	 * The role delimiter. 
61  	 * @deprecated This will be removed in a release after Struts 1.2.
62  	 */
63  	public static final String ROLE_DELIMITER = ",";
64  
65  	/*** 
66  	 * Commons Logging instance. 
67  	 */
68  	protected static Log log = LogFactory.getLog(InsertTag.class);
69  
70  	/* JSP Tag attributes */
71  
72  	/*** 
73  	 * Flush attribute value. 
74  	 */
75  	protected boolean flush = true;
76  
77  	/*** 
78  	 * Name to insert. 
79  	 */
80  	protected String name = null;
81  
82  	/***
83  	 * Name of attribute from which to read page name to include. 
84  	 */
85  	protected String attribute = null;
86  
87  	/*** 
88  	 * Name of bean used as entity to include. 
89  	 */
90  	protected String beanName = null;
91  
92  	/*** 
93  	 * Name of bean property, if any. 
94  	 */
95  	protected String beanProperty = null;
96  
97  	/*** 
98  	 * Scope of bean, if any. 
99  	 */
100 	protected String beanScope = null;
101 
102 	/***
103 	 * Are errors ignored. This is the property for attribute 'ignore'.
104 	 * Default value is false, which throw an exception.
105 	 * Only 'attribute not found' errors are ignored.
106 	 */
107 	protected boolean isErrorIgnored = false;
108 
109 	/***
110 	 * Name of component instance to include.
111 	 */
112 	protected String definitionName = null;
113 
114 	/* Internal properties */
115 	/***
116 	 * Does the end tag need to be processed.
117 	 * Default value is true. Boolean set in case of ignored errors.
118 	 */
119 	protected boolean processEndTag = true;
120 
121 	/*** 
122 	 * Current component context. 
123 	 */
124 	protected ComponentContext cachedCurrentContext;
125 
126 	/*** 
127 	 * Final handler of tag methods. 
128 	 */
129 	protected TagHandler tagHandler = null;
130 
131 	/*** 
132 	 * Trick to allows inner classes to access pageContext. 
133 	 */
134 	protected PageContext pageContext = null;
135 
136 	/***
137 	 * Reset member values for reuse. This method calls super.release(),
138 	 * which invokes TagSupport.release(), which typically does nothing.
139 	 */
140 	public void release() {
141 
142 		super.release();
143 		attribute = null;
144 		beanName = null;
145 		beanProperty = null;
146 		beanScope = null;
147 
148 		definitionName = null;
149 		flush = true;
150 		name = null;
151 		page = null;
152 		role = null;
153 		isErrorIgnored = false;
154 
155 		releaseInternal();
156 	}
157 
158 	/***
159 	 * Reset internal member values for reuse.
160 	 */
161 	protected void releaseInternal() {
162 		cachedCurrentContext = null;
163 		processEndTag = true;
164 		// pageContext = null;  // orion doesn't set it between two tags
165 		tagHandler = null;
166 	}
167 
168 	/***
169 	 * Set the current page context.
170 	 * Called by the page implementation prior to doStartTag().
171 	 * <p>
172 	 * Needed to allow inner classes to access pageContext.
173 	 */
174 	public void setPageContext(PageContext pc) {
175 		this.pageContext = pc;
176 		super.setPageContext(pc);
177 	}
178 
179 	/***
180 	 * Get the pageContext property.
181 	 */
182 	public PageContext getPageContext() {
183 		return pageContext;
184 	}
185 
186 	/***
187 	 * Set name.
188 	 */
189 	public void setName(String value) {
190 		this.name = value;
191 	}
192 
193 	/***
194 	 * Get name.
195 	 */
196 	public String getName() {
197 		return name;
198 	}
199 
200 	/***
201 	 * Set component.
202 	 */
203 	public void setComponent(String name) {
204 		this.page = name;
205 	}
206 
207 	/***
208 	 * Set definition.
209 	 */
210 	public void setDefinition(String name) {
211 		this.definitionName = name;
212 	}
213 
214 	/***
215 	 * Get definition name.
216 	 */
217 	public String getDefinitionName() {
218 		return definitionName;
219 	}
220 
221 	/***
222 	 * Set attribute.
223 	 */
224 	public void setAttribute(String value) {
225 		this.attribute = value;
226 	}
227 
228 	/***
229 	 * Set bean name.
230 	 */
231 	public void setBeanName(String value) {
232 		this.beanName = value;
233 	}
234 
235 	/***
236 	 * Get bean name.
237 	 */
238 	public String getBeanName() {
239 		return beanName;
240 	}
241 
242 	/***
243 	 * Set bean property.
244 	 */
245 	public void setBeanProperty(String value) {
246 		this.beanProperty = value;
247 	}
248 
249 	/***
250 	 * Get bean property.
251 	 */
252 	public String getBeanProperty() {
253 		return beanProperty;
254 	}
255 
256 	/***
257 	 * Set bean scope.
258 	 */
259 	public void setBeanScope(String value) {
260 		this.beanScope = value;
261 	}
262 
263 	/***
264 	 * Get bean scope.
265 	 */
266 	public String getBeanScope() {
267 		return beanScope;
268 	}
269 
270 	/***
271 	 * Set flush.
272 	 */
273 	public void setFlush(boolean flush) {
274 		this.flush = flush;
275 	}
276 
277 	/***
278 	 * Get flush.
279 	 */
280 	public boolean getFlush() {
281 		return flush;
282 	}
283 
284 	/***
285 	 * Set flush.
286 	 * Method added for compatibility with JSP1.1
287 	 */
288 	public void setFlush(String flush) {
289 		this.flush = (Boolean.valueOf(flush).booleanValue());
290 	}
291 
292 	/***
293 	 * Set ignore.
294 	 */
295 	public void setIgnore(boolean ignore) {
296 		this.isErrorIgnored = ignore;
297 	}
298 
299 	/***
300 	 * Get ignore.
301 	 */
302 	public boolean getIgnore() {
303 		return isErrorIgnored;
304 	}
305 
306 	/////////////////////////////////////////////////////////////////////////
307 
308 	/***
309 	 * Add a body attribute.
310 	 * Erase any attribute with same name previously set.
311 	 */
312 	public void putAttribute(String name, Object value) {
313 		tagHandler.putAttribute(name, value);
314 	}
315 
316 	/***
317 	 * Process nested &lg;put&gt; tag.
318 	 * Method calls by nested &lg;put&gt; tags.
319 	 * Nested list is added to current list.
320 	 * If role is defined, it is checked immediately.
321 	 */
322 	public void processNestedTag(PutTag nestedTag) throws JspException {
323 		// Check role
324 		HttpServletRequest request =
325 			(HttpServletRequest) pageContext.getRequest();
326 		String role = nestedTag.getRole();
327 		if (role != null && !request.isUserInRole(role)) {
328 			// not allowed : skip attribute
329 			return;
330 		}
331 
332 		putAttribute(nestedTag.getName(), nestedTag.getRealValue());
333 	}
334 
335 	/***
336 	 * Process nested &lg;putList&gt; tag.
337 	 * Method calls by nested &lg;putList&gt; tags.
338 	 * Nested list is added to sub-component attributes
339 	 * If role is defined, it is checked immediately.
340 	 */
341 	public void processNestedTag(PutListTag nestedTag) throws JspException {
342 		// Check role
343 		HttpServletRequest request =
344 			(HttpServletRequest) pageContext.getRequest();
345 		String role = nestedTag.getRole();
346 		if (role != null && !request.isUserInRole(role)) {
347 			// not allowed : skip attribute
348 			return;
349 		}
350 
351 		// Check if a name is defined
352 		if (nestedTag.getName() == null) {
353 			throw new JspException("Error - PutList : attribute name is not defined. It is mandatory as the list is added as attribute of 'insert'.");
354 		}
355 
356 		// now add attribute to enclosing parent (i.e. : this object).
357 		putAttribute(nestedTag.getName(), nestedTag.getList());
358 	}
359 
360 	/***
361 	 * Method calls by nested &lg;putList&gt; tags.
362 	 * A new list is added to current insert object.
363 	 */
364 	public void putAttribute(PutListTag nestedTag) throws JspException {
365 		// Check role
366 		HttpServletRequest request =
367 			(HttpServletRequest) pageContext.getRequest();
368 		String role = nestedTag.getRole();
369 		if (role != null && !request.isUserInRole(role)) {
370 			// not allowed : skip attribute
371 			return;
372 		}
373 
374 		putAttribute(nestedTag.getName(), nestedTag.getList());
375 	}
376 
377 	/***
378 	 * Get current component context.
379 	 */
380 	private ComponentContext getCurrentContext() {
381 		if (cachedCurrentContext == null) {
382 			cachedCurrentContext =
383 				(ComponentContext) pageContext.getAttribute(
384 					ComponentConstants.COMPONENT_CONTEXT,
385 					PageContext.REQUEST_SCOPE);
386 		}
387 
388 		return cachedCurrentContext;
389 	}
390 
391 	/***
392 	 * Get instantiated Controller.
393 	 * Return controller denoted by controllerType, or <code>null</code> if controllerType
394 	 * is null.
395 	 * @throws JspException If controller can't be created.
396 	 */
397 	private Controller getController() throws JspException {
398 		if (controllerType == null) {
399 			return null;
400 		}
401 
402 		try {
403 			return ComponentDefinition.createController(
404 				controllerName,
405 				controllerType);
406 
407 		} catch (InstantiationException ex) {
408 			throw new JspException(ex);
409 		}
410 	}
411 
412 	/***
413 	 * Process the start tag by checking tag's attributes and creating appropriate handler.
414 	 * Possible handlers :
415 	 * <ul>
416 	 * <li> URL
417 	 * <li> definition
418 	 * <li> direct String
419 	 * </ul>
420 	 * Handlers also contain sub-component context.
421 	 */
422 	public int doStartTag() throws JspException {
423 
424             // Additional fix for Bug 20034 (2005-04-28)
425             cachedCurrentContext = null;
426 
427 		// Check role immediatly to avoid useless stuff.
428 		// In case of insertion of a "definition", definition's role still checked later.
429 		// This lead to a double check of "role" ;-(
430 		HttpServletRequest request =
431 			(HttpServletRequest) pageContext.getRequest();
432 		if (role != null && !request.isUserInRole(role)) {
433 			processEndTag = false;
434 			return SKIP_BODY;
435 		}
436 
437 		try {
438 			tagHandler = createTagHandler();
439 
440 		} catch (JspException e) {
441 			if (isErrorIgnored) {
442 				processEndTag = false;
443 				return SKIP_BODY;
444 			} else {
445 				throw e;
446 			}
447 		}
448 
449 		return tagHandler.doStartTag();
450 	}
451 
452 	/***
453 	 * Process the end tag by including the template.
454 	 * Simply call the handler doEndTag
455 	 */
456 	public int doEndTag() throws JspException {
457 		if (!processEndTag) {
458 			releaseInternal();
459 			return EVAL_PAGE;
460 		}
461 
462 		int res = tagHandler.doEndTag();
463 		// Reset properties used by object, in order to be able to reuse object.
464 		releaseInternal();
465 		return res;
466 	}
467 
468 	/***
469 	 * Process tag attribute and create corresponding tag handler.
470 	 */
471 	public TagHandler createTagHandler() throws JspException {
472 		// Check each tag attribute.
473 		// page Url attribute must be the last checked  because it can appears concurrently
474 		// with others attributes.
475 		if (definitionName != null) {
476 			return processDefinitionName(definitionName);
477 		} else if (attribute != null) {
478 			return processAttribute(attribute);
479 		} else if (beanName != null) {
480 			return processBean(beanName, beanProperty, beanScope);
481 		} else if (name != null) {
482 			return processName(name);
483 		} else if (page != null) {
484 			return processUrl(page);
485 		} else {
486 			throw new JspException("Error - Tag Insert : At least one of the following attribute must be defined : template|page|attribute|definition|name|beanName. Check tag syntax");
487 		}
488 	}
489 
490 	/***
491 	 * Process an object retrieved as a bean or attribute.
492 	 * Object can be a typed attribute, a String, or anything else.
493 	 * If typed attribute, use associated type.
494 	 * Otherwise, apply toString() on object, and use returned string as a name.
495 	 * @throws JspException - Throws by underlying nested call to 
496 	 * processDefinitionName()
497 	 */
498 	public TagHandler processObjectValue(Object value) throws JspException {
499 		// First, check if value is one of the Typed Attribute
500 		if (value instanceof AttributeDefinition) {
501 			// We have a type => return appropriate IncludeType
502 			return processTypedAttribute((AttributeDefinition) value);
503 
504 		} else if (value instanceof ComponentDefinition) {
505 			return processDefinition((ComponentDefinition) value);
506 		}
507 
508 		// Value must denote a valid String
509 		return processAsDefinitionOrURL(value.toString());
510 	}
511 
512 	/***
513 	 * Process name.
514 	 * Search in following order :
515 	 * <ul>
516 	 * <li>Component context -  if found, process it as value.</li>
517 	 * <li>definitions factory</li>
518 	 * <li>URL</li>
519 	 * <li></li>
520 	 * </ul>
521 	 *
522 	 * @return appropriate tag handler.
523 	 * @throws JspException - Throws by underlying nested call to 
524 	 * processDefinitionName()
525 	 */
526 	public TagHandler processName(String name) throws JspException {
527 		Object attrValue = getCurrentContext().getAttribute(name);
528 
529 		if (attrValue != null) {
530 			return processObjectValue(attrValue);
531 		}
532 
533 		return processAsDefinitionOrURL(name);
534 	}
535 
536 	/***
537 	 * Process the url.
538 	 * @throws JspException If failed to create controller
539 	 */
540 	public TagHandler processUrl(String url) throws JspException {
541 		return new InsertHandler(url, role, getController());
542 	}
543 
544 	/***
545 	 * Process tag attribute "definition".
546 	 * First, search definition in the factory, then create handler from this definition.
547 	 * @param name Name of the definition.
548 	 * @return Appropriate TagHandler.
549 	 * @throws JspException- NoSuchDefinitionException No Definition  found for name.
550 	 * @throws JspException- FactoryNotFoundException Can't find Definitions factory.
551 	 * @throws JspException- DefinedComponentFactoryException General error in factory.
552 	 * @throws JspException InstantiationException Can't create requested controller
553 	 */
554 	protected TagHandler processDefinitionName(String name)
555 		throws JspException {
556 
557 		try {
558 			ComponentDefinition definition =
559 				TilesUtil.getDefinition(
560 					name,
561 					(HttpServletRequest) pageContext.getRequest(),
562 					pageContext.getServletContext());
563 
564 			if (definition == null) { // is it possible ?
565 				throw new NoSuchDefinitionException();
566 			}
567 
568 			return processDefinition(definition);
569 
570 		} catch (NoSuchDefinitionException ex) {
571 			throw new JspException(
572 				"Error -  Tag Insert : Can't get definition '"
573 					+ definitionName
574 					+ "'. Check if this name exist in definitions factory.", ex);
575 
576 		} catch (FactoryNotFoundException ex) {
577 			throw new JspException(ex.getMessage());
578 
579 		} catch (DefinitionsFactoryException ex) {
580 			if (log.isDebugEnabled()) {
581 				ex.printStackTrace();
582 			}
583 
584 			// Save exception to be able to show it later
585 			pageContext.setAttribute(
586 				Globals.EXCEPTION_KEY,
587 				ex,
588 				PageContext.REQUEST_SCOPE);
589 			throw new JspException(ex);
590 		}
591 	}
592 
593 	/***
594 	 * End of Process tag attribute "definition".
595 	 * Overload definition with tag attributes "template" and "role".
596 	 * Then, create appropriate tag handler.
597 	 * @param definition Definition to process.
598 	 * @return Appropriate TagHandler.
599 	 * @throws JspException InstantiationException Can't create requested controller
600 	 */
601 	protected TagHandler processDefinition(ComponentDefinition definition)
602 		throws JspException {
603 		// Declare local variable in order to not change Tag attribute values.
604 		String role = this.role;
605 		String page = this.page;
606 		Controller controller = null;
607 
608 		try {
609 			controller = definition.getOrCreateController();
610 
611 			// Overload definition with tag's template and role.
612 			if (role == null) {
613 				role = definition.getRole();
614 			}
615 
616 			if (page == null) {
617 				page = definition.getTemplate();
618 			}
619 
620 			if (controllerName != null) {
621 				controller =
622 					ComponentDefinition.createController(
623 						controllerName,
624 						controllerType);
625 			}
626 
627 			// Can check if page is set
628 			return new InsertHandler(
629 				definition.getAttributes(),
630 				page,
631 				role,
632 				controller);
633 
634 		} catch (InstantiationException ex) {
635 			throw new JspException(ex);
636 		}
637 	}
638 
639 	/***
640 	 * Process a bean.
641 	 * Get bean value, eventually using property and scope. Found value is process by processObjectValue().
642 	 * @param beanName Name of the bean
643 	 * @param beanProperty Property in the bean, or null.
644 	 * @param beanScope bean scope, or null.
645 	 * @return Appropriate TagHandler.
646 	 * @throws JspException - NoSuchDefinitionException No value associated to bean.
647 	 * @throws JspException an error occur while reading bean, or no definition found.
648 	 * @throws JspException - Throws by underlying nested call to processDefinitionName()
649 	 */
650 	protected TagHandler processBean(
651 		String beanName,
652 		String beanProperty,
653 		String beanScope)
654 		throws JspException {
655 
656 		Object beanValue =
657 			TagUtils.getRealValueFromBean(
658 				beanName,
659 				beanProperty,
660 				beanScope,
661 				pageContext);
662 
663 		if (beanValue == null) {
664 			throw new JspException(
665 				"Error - Tag Insert : No value defined for bean '"
666 					+ beanName
667 					+ "' with property '"
668 					+ beanProperty
669 					+ "' in scope '"
670 					+ beanScope
671 					+ "'.");
672 		}
673 
674 		return processObjectValue(beanValue);
675 	}
676 
677 	/***
678 	 * Process tag attribute "attribute".
679 	 * Get value from component attribute.
680 	 * Found value is process by processObjectValue().
681 	 * @param name Name of the attribute.
682 	 * @return Appropriate TagHandler.
683 	 * @throws JspException - NoSuchDefinitionException No Definition  found for name.
684 	 * @throws JspException - Throws by underlying nested call to processDefinitionName()
685 	 */
686 	public TagHandler processAttribute(String name) throws JspException {
687 		Object attrValue = getCurrentContext().getAttribute(name);
688 
689 		if (attrValue == null) {
690 			throw new JspException(
691 				"Error - Tag Insert : No value found for attribute '"
692 					+ name
693 					+ "'.");
694 		}
695 
696 		return processObjectValue(attrValue);
697 	}
698 
699 	/***
700 	 * Try to process name as a definition, or as an URL if not found.
701 	 * @param name Name to process.
702 	 * @return appropriate TagHandler
703 	 * @throws JspException InstantiationException Can't create requested controller
704 	 */
705 	public TagHandler processAsDefinitionOrURL(String name)
706 		throws JspException {
707 		try {
708 			ComponentDefinition definition =
709 				TilesUtil.getDefinition(
710 					name,
711 					pageContext.getRequest(),
712 					pageContext.getServletContext());
713 
714 			if (definition != null) {
715 				return processDefinition(definition);
716 			}
717 
718 		} catch (DefinitionsFactoryException ex) {
719 			// silently failed, because we can choose to not define a factory.
720 		}
721 
722 		// no definition found, try as url
723 		return processUrl(name);
724 	}
725 
726 	/***
727 	 * Process typed attribute according to its type.
728 	 * @param value Typed attribute to process.
729 	 * @return appropriate TagHandler.
730 	 * @throws JspException - Throws by underlying nested call to processDefinitionName()
731 	 */
732 	public TagHandler processTypedAttribute(AttributeDefinition value)
733 		throws JspException {
734 		if (value instanceof DirectStringAttribute) {
735 			return new DirectStringHandler((String) value.getValue());
736 
737 		} else if (value instanceof DefinitionAttribute) {
738 			return processDefinition((ComponentDefinition) value.getValue());
739 
740 		} else if (value instanceof DefinitionNameAttribute) {
741 			return processDefinitionName((String) value.getValue());
742 		}
743 
744 		return new InsertHandler(
745 			(String) value.getValue(),
746 			role,
747 			getController());
748 	}
749 
750 	/***
751 	 * Do an include of specified page.
752 	 * This method is used internally to do all includes from this class. It delegates
753 	 * the include call to the TilesUtil.doInclude().
754 	 * @param page The page that will be included
755      * @param flush If the writer should be flushed before the include
756 	 * @throws ServletException - Thrown by call to pageContext.include()
757 	 * @throws IOException - Thrown by call to pageContext.include()
758 	 */
759 	protected void doInclude(String page, boolean flush)
760 		throws ServletException, IOException {
761 		TilesUtil.doInclude(page, pageContext, flush);
762 	}
763 
764 	/////////////////////////////////////////////////////////////////////////////
765 
766 	/***
767 	 * Inner Interface.
768 	 * Sub handler for tag.
769 	 */
770 	protected interface TagHandler {
771 		/***
772 		 * Create ComponentContext for type depicted by implementation class.
773 		 */
774 		public int doStartTag() throws JspException;
775 		/***
776 		 * Do include for type depicted by implementation class.
777 		 */
778 		public int doEndTag() throws JspException;
779 		/***
780 		 * Add a component parameter (attribute) to subContext.
781 		 */
782 		public void putAttribute(String name, Object value);
783 	} // end inner interface
784 
785 	/////////////////////////////////////////////////////////////////////////////
786 
787 	/***
788 	 * Real handler, after attribute resolution.
789 	 * Handle include sub-component.
790 	 */
791 	protected class InsertHandler implements TagHandler {
792 		protected String page;
793 		protected ComponentContext currentContext;
794 		protected ComponentContext subCompContext;
795 		protected String role;
796 		protected Controller controller;
797 
798 		/***
799 		 * Constructor.
800 		 * Create insert handler using Component definition.
801 		 */
802 		public InsertHandler(
803 			Map attributes,
804 			String page,
805 			String role,
806 			Controller controller) {
807 
808 			this.page = page;
809 			this.role = role;
810 			this.controller = controller;
811 			subCompContext = new ComponentContext(attributes);
812 		}
813 
814 		/***
815 		 * Constructor.
816 		 * Create insert handler to insert page at specified location.
817 		 */
818 		public InsertHandler(String page, String role, Controller controller) {
819 			this.page = page;
820 			this.role = role;
821 			this.controller = controller;
822 			subCompContext = new ComponentContext();
823 		}
824 
825 		/***
826 		 * Create a new empty context.
827 		 */
828 		public int doStartTag() throws JspException {
829 			// Check role
830 			HttpServletRequest request =
831 				(HttpServletRequest) pageContext.getRequest();
832 
833 			if (role != null && !request.isUserInRole(role)) {
834 				return SKIP_BODY;
835 			}
836 
837 			// save current context
838 			this.currentContext = getCurrentContext();
839 			return EVAL_BODY_INCLUDE;
840 		}
841 
842 		/***
843 		 * Add attribute to sub context.
844 		 * Do nothing.
845 		 */
846 		public void putAttribute(String name, Object value) {
847 			subCompContext.putAttribute(name, value);
848 		}
849 
850 		/***
851 		 * Include requested page.
852 		 */
853 		public int doEndTag() throws JspException {
854 			// Check role
855 			HttpServletRequest request =
856 				(HttpServletRequest) pageContext.getRequest();
857 
858 			if (role != null && !request.isUserInRole(role)) {
859 				return EVAL_PAGE;
860 			}
861 
862 			try {
863 				if (log.isDebugEnabled()) {
864 					log.debug("insert page='" + page + "'.");
865 				}
866 
867 				// set new context for included component.
868 				pageContext.setAttribute(
869 					ComponentConstants.COMPONENT_CONTEXT,
870 					subCompContext,
871 					PageContext.REQUEST_SCOPE);
872 
873 				// Call controller if any
874 				if (controller != null) {
875 					try {
876 						controller.execute(
877 							subCompContext,
878 							(HttpServletRequest) pageContext.getRequest(),
879 							(HttpServletResponse) pageContext.getResponse(),
880 							pageContext.getServletContext());
881                             
882 					} catch (Exception e) {
883 						throw new ServletException(e);
884 					}
885 
886 				}
887 
888 				// include requested component.
889 				if (flush) {
890 					pageContext.getOut().flush();
891 				}
892 
893 				doInclude(page, flush);
894 
895 			} catch (IOException e) {
896 				String msg =
897 					"Can't insert page '" + page + "' : " + e.getMessage();
898 				log.error(msg, e);
899 				throw new JspException(msg);
900 
901 			} catch (IllegalArgumentException e) {
902 				// Can't resolve page uri, should we ignore it?
903 				if (!(page == null && isErrorIgnored)) {
904 					String msg =
905 						"Can't insert page '"
906 							+ page
907 							+ "'. Check if it exists.\n"
908 							+ e.getMessage();
909 
910 					log.error(msg, e);
911 					throw new JspException(msg,e);
912 				}
913 
914 			} catch (ServletException e) {
915 				Throwable cause = e;
916 				if (e.getRootCause() != null) {
917 					cause = e.getRootCause();
918 				}
919 
920 				String msg =
921 					"ServletException in '" + page + "': " + cause.getMessage();
922 
923 				log.error(msg, e);
924 				throw new JspException(msg,e);
925 
926 			} finally {
927 				// restore old context only if currentContext not null 
928 				// (bug with Silverstream ?; related by Arvindra Sehmi 20010712)
929 				if (currentContext != null) {
930 					pageContext.setAttribute(
931 						ComponentConstants.COMPONENT_CONTEXT,
932 						currentContext,
933 						PageContext.REQUEST_SCOPE);
934 				}
935 			}
936 
937 			return EVAL_PAGE;
938 		}
939 
940 		/***
941 		 * Process an exception.
942 		 * Depending of debug attribute, print full exception trace or only
943 		 * its message in output page.
944 		 * @param ex Exception
945 		 * @param msg An additional message to show in console and to propagate if we can't output exception.
946 		 * @deprecated This method will be removed in a release after Struts 1.2.
947 		 */
948 		protected void processException(Throwable ex, String msg)
949 			throws JspException {
950 
951 			try {
952 				if (msg == null) {
953 					msg = ex.getMessage();
954 				}
955 
956 				if (log.isDebugEnabled()) { // show full trace
957 					log.debug(msg, ex);
958 					pageContext.getOut().println(msg);
959 					ex.printStackTrace(
960 						new PrintWriter(pageContext.getOut(), true));
961 				} else { // show only message
962 					pageContext.getOut().println(msg);
963 				}
964 
965 			} catch (IOException ioex) { // problems. Propagate original exception
966 				pageContext.setAttribute(
967 					ComponentConstants.EXCEPTION_KEY,
968 					ex,
969 					PageContext.REQUEST_SCOPE);
970 				throw new JspException(msg,ioex);
971 			}
972 		}
973 	}
974 
975 	/***
976 	 * Parse the list of roles and return <code>true</code> or <code>false</code> based on whether
977 	 * the user has that role or not.
978 	 * @param role Comma-delimited list of roles.
979 	 * @param request The request.
980 	 */
981 	static public boolean userHasRole(
982 		HttpServletRequest request,
983 		String role) {
984 		StringTokenizer st = new StringTokenizer(role, ",");
985 		while (st.hasMoreTokens()) {
986 			if (request.isUserInRole(st.nextToken())) {
987 				return true;
988 			}
989 		}
990 
991 		return false;
992 	}
993 
994 	/////////////////////////////////////////////////////////////////////////////
995 
996 	/***
997 	 * Handle insert direct string.
998 	 */
999 	protected class DirectStringHandler implements TagHandler {
1000 		/*** Object to print as a direct string */
1001 		private Object value;
1002 
1003 		/***
1004 		 * Constructor.
1005 		 */
1006 		public DirectStringHandler(Object value) {
1007 			this.value = value;
1008 		}
1009 
1010 		/***
1011 		 * Do nothing, there is no context for a direct string.
1012 		 */
1013 		public int doStartTag() throws JspException {
1014 			return SKIP_BODY;
1015 		}
1016 
1017 		/***
1018 		 * Add attribute to sub context.
1019 		 * Do nothing.
1020 		 */
1021 		public void putAttribute(String name, Object value) {
1022 		}
1023 
1024 		/***
1025 		 * Print String in page output stream.
1026 		 */
1027 		public int doEndTag() throws JspException {
1028 			try {
1029 				if (flush) {
1030 					pageContext.getOut().flush();
1031 				}
1032 
1033 				pageContext.getOut().print(value);
1034 
1035 			} catch (IOException ex) {
1036 				if (log.isDebugEnabled()) {
1037 					log.debug("Can't write string '" + value + "' : ", ex);
1038 				}
1039 
1040 				pageContext.setAttribute(
1041 					ComponentConstants.EXCEPTION_KEY,
1042 					ex,
1043 					PageContext.REQUEST_SCOPE);
1044 
1045 				throw new JspException(
1046 					"Can't write string '" + value + "' : " + ex.getMessage(), ex);
1047 			}
1048 
1049 			return EVAL_PAGE;
1050 		}
1051 	}
1052 }