0001: /*
0002: * $Id: ActionConfig.java 480593 2006-11-29 15:17:52Z niallp $
0003: *
0004: * Licensed to the Apache Software Foundation (ASF) under one
0005: * or more contributor license agreements. See the NOTICE file
0006: * distributed with this work for additional information
0007: * regarding copyright ownership. The ASF licenses this file
0008: * to you under the Apache License, Version 2.0 (the
0009: * "License"); you may not use this file except in compliance
0010: * with the License. You may obtain a copy of the License at
0011: *
0012: * http://www.apache.org/licenses/LICENSE-2.0
0013: *
0014: * Unless required by applicable law or agreed to in writing,
0015: * software distributed under the License is distributed on an
0016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0017: * KIND, either express or implied. See the License for the
0018: * specific language governing permissions and limitations
0019: * under the License.
0020: */
0021: package org.apache.struts.config;
0022:
0023: import org.apache.commons.beanutils.BeanUtils;
0024: import org.apache.commons.logging.Log;
0025: import org.apache.commons.logging.LogFactory;
0026: import org.apache.struts.util.RequestUtils;
0027:
0028: import java.lang.reflect.InvocationTargetException;
0029:
0030: import java.util.ArrayList;
0031: import java.util.HashMap;
0032:
0033: /**
0034: * <p>A JavaBean representing the configuration information of an
0035: * <code><action></code> element from a Struts module configuration
0036: * file.</p>
0037: *
0038: * @version $Rev: 480593 $ $Date: 2006-11-29 09:17:52 -0600 (Wed, 29 Nov 2006) $
0039: * @since Struts 1.1
0040: */
0041: public class ActionConfig extends BaseConfig {
0042: private static final Log log = LogFactory
0043: .getLog(ActionConfig.class);
0044:
0045: // ----------------------------------------------------- Instance Variables
0046:
0047: /**
0048: * <p> The set of exception handling configurations for this action, if
0049: * any, keyed by the <code>type</code> property. </p>
0050: */
0051: protected HashMap exceptions = new HashMap();
0052:
0053: /**
0054: * <p> The set of local forward configurations for this action, if any,
0055: * keyed by the <code>name</code> property. </p>
0056: */
0057: protected HashMap forwards = new HashMap();
0058:
0059: // ------------------------------------------------------------- Properties
0060:
0061: /**
0062: * <p> The module configuration with which we are associated. </p>
0063: */
0064: protected ModuleConfig moduleConfig = null;
0065:
0066: /**
0067: * <p> The request-scope or session-scope attribute name under which our
0068: * form bean is accessed, if it is different from the form bean's
0069: * specified <code>name</code>. </p>
0070: */
0071: protected String attribute = null;
0072:
0073: /**
0074: * <p>The internal identification of this action mapping. Identifications are
0075: * not inheritable and must be unique within a module.</p>
0076: *
0077: * @since Struts 1.3.6
0078: */
0079: protected String actionId = null;
0080:
0081: /**
0082: * <p>The path of the ActionConfig that this object should inherit
0083: * properties from.</p> </p>
0084: */
0085: protected String inherit = null;
0086:
0087: /**
0088: * Indicates whether the "cancellable " property has been set or not.
0089: */
0090: private boolean cancellableSet = false;
0091:
0092: /**
0093: * <p>Can this Action be cancelled? [false]</p> <p> By default, when an
0094: * Action is cancelled, validation is bypassed and the Action should not
0095: * execute the business operation. If a request tries to cancel an Action
0096: * when cancellable is not set, a "InvalidCancelException" is thrown.
0097: * </p>
0098: */
0099: protected boolean cancellable = false;
0100:
0101: /**
0102: * <p> Have the inheritance values for this class been applied?</p>
0103: */
0104: protected boolean extensionProcessed = false;
0105:
0106: /**
0107: * <p> Context-relative path of the web application resource that will
0108: * process this request via RequestDispatcher.forward(), instead of
0109: * instantiating and calling the <code>Action</code> class specified by
0110: * "type". Exactly one of <code>forward</code>, <code>include</code>, or
0111: * <code>type</code> must be specified. </p>
0112: */
0113: protected String forward = null;
0114:
0115: /**
0116: * <p> Context-relative path of the web application resource that will
0117: * process this request via RequestDispatcher.include(), instead of
0118: * instantiating and calling the <code>Action</code> class specified by
0119: * "type". Exactly one of <code>forward</code>, <code>include</code>, or
0120: * <code>type</code> must be specified. </p>
0121: */
0122: protected String include = null;
0123:
0124: /**
0125: * <p> Context-relative path of the input form to which control should be
0126: * returned if a validation error is encountered. Required if "name" is
0127: * specified and the input bean returns validation errors. </p>
0128: */
0129: protected String input = null;
0130:
0131: /**
0132: * <p> Fully qualified Java class name of the <code>MultipartRequestHandler</code>
0133: * implementation class used to process multi-part request data for this
0134: * Action. </p>
0135: */
0136: protected String multipartClass = null;
0137:
0138: /**
0139: * <p> Name of the form bean, if any, associated with this Action. </p>
0140: */
0141: protected String name = null;
0142:
0143: /**
0144: * <p> General purpose configuration parameter that can be used to pass
0145: * extra information to the Action instance selected by this Action.
0146: * Struts does not itself use this value in any way. </p>
0147: */
0148: protected String parameter = null;
0149:
0150: /**
0151: * <p> Context-relative path of the submitted request, starting with a
0152: * slash ("/") character, and omitting any filename extension if extension
0153: * mapping is being used. </p>
0154: */
0155: protected String path = null;
0156:
0157: /**
0158: * <p> Prefix used to match request parameter names to form bean property
0159: * names, if any. </p>
0160: */
0161: protected String prefix = null;
0162:
0163: /**
0164: * <p> Comma-delimited list of security role names allowed to request this
0165: * Action. </p>
0166: */
0167: protected String roles = null;
0168:
0169: /**
0170: * <p> The set of security role names used to authorize access to this
0171: * Action, as an array for faster access. </p>
0172: */
0173: protected String[] roleNames = new String[0];
0174:
0175: /**
0176: * <p> Identifier of the scope ("request" or "session") within which our
0177: * form bean is accessed, if any. </p>
0178: */
0179: protected String scope = "session";
0180:
0181: /**
0182: * <p> Suffix used to match request parameter names to form bean property
0183: * names, if any. </p>
0184: */
0185: protected String suffix = null;
0186:
0187: /**
0188: * <p> Fully qualified Java class name of the <code>Action</code> class to
0189: * be used to process requests for this mapping if the
0190: * <code>forward</code> and <code>include</code> properties are not set.
0191: * Exactly one of <code>forward</code>, <code>include</code>, or
0192: * <code>type</code> must be specified.
0193: */
0194: protected String type = null;
0195:
0196: /**
0197: * <p> Indicates Action be configured as the default one for this module,
0198: * when true.
0199: */
0200: protected boolean unknown = false;
0201:
0202: /**
0203: * Indicates whether the "validate" property has been set or not.
0204: */
0205: private boolean validateSet = false;
0206:
0207: /**
0208: * <p> Should the <code>validate()</code> method of the form bean
0209: * associated with this action be called?
0210: */
0211: protected boolean validate = true;
0212:
0213: /**
0214: * <p> The name of a <code>commons-chain</code> command which should be
0215: * executed as part of the processing of this action.
0216: *
0217: * @since Struts 1.3.0
0218: */
0219: protected String command = null;
0220:
0221: /**
0222: * <p> The name of a <code>commons-chain</code> catalog in which
0223: * <code>command</code> should be sought. If a <code>command</code> is
0224: * defined and this property is undefined, the "default" catalog will be
0225: * used. This is likely to be infrequently used after a future release of
0226: * <code>commons-chain</code> supports a one-string expression of a
0227: * catalog/chain combination.
0228: *
0229: * @since Struts 1.3.0
0230: */
0231: protected String catalog = null;
0232:
0233: /**
0234: * <p>The internal name of this action mapping. If an action has a name, it may be used
0235: * as a shortcut in a URI. For example, an action with an identification of "editPerson"
0236: * may be internally forwarded as "editPerson?id=1" which will then resolve to the
0237: * real URI path at execution time.</p>
0238: *
0239: * @return the actionId
0240: * @since Struts 1.3.6
0241: */
0242: public String getActionId() {
0243: return this .actionId;
0244: }
0245:
0246: /**
0247: * <p>The internal name of this action mapping. The name is not inheritable,
0248: * may not contain a forward slash, and must be unique within a module. </p>
0249: *
0250: * @param actionId the action identifier
0251: * @since Struts 1.3.6
0252: * @throws IllegalStateException if the configuration is frozen
0253: * @throws IllegalArgumentException if the identifier contains a forward slash
0254: */
0255: public void setActionId(String actionId) {
0256: if (configured) {
0257: throw new IllegalStateException("Configuration is frozen");
0258: }
0259:
0260: if ((actionId != null) && (actionId.indexOf("/") > -1)) {
0261: throw new IllegalArgumentException("actionId '" + actionId
0262: + "' may not contain a forward slash");
0263: }
0264:
0265: this .actionId = actionId;
0266: }
0267:
0268: /**
0269: * <p> The module configuration with which we are associated.
0270: */
0271: public ModuleConfig getModuleConfig() {
0272: return (this .moduleConfig);
0273: }
0274:
0275: /**
0276: * <p> The module configuration with which we are associated.
0277: */
0278: public void setModuleConfig(ModuleConfig moduleConfig) {
0279: if (configured) {
0280: throw new IllegalStateException("Configuration is frozen");
0281: }
0282:
0283: this .moduleConfig = moduleConfig;
0284: }
0285:
0286: /**
0287: * <p> Returns the request-scope or session-scope attribute name under
0288: * which our form bean is accessed, if it is different from the form
0289: * bean's specified <code>name</code>.
0290: *
0291: * @return attribute name under which our form bean is accessed.
0292: */
0293: public String getAttribute() {
0294: if (this .attribute == null) {
0295: return (this .name);
0296: } else {
0297: return (this .attribute);
0298: }
0299: }
0300:
0301: /**
0302: * <p> Set the request-scope or session-scope attribute name under which
0303: * our form bean is accessed, if it is different from the form bean's
0304: * specified <code>name</code>.
0305: *
0306: * @param attribute the request-scope or session-scope attribute name
0307: * under which our form bean is access.
0308: */
0309: public void setAttribute(String attribute) {
0310: if (configured) {
0311: throw new IllegalStateException("Configuration is frozen");
0312: }
0313:
0314: this .attribute = attribute;
0315: }
0316:
0317: /**
0318: * <p>Accessor for cancellable property</p>
0319: *
0320: * @return True if Action can be cancelled
0321: */
0322: public boolean getCancellable() {
0323: return (this .cancellable);
0324: }
0325:
0326: /**
0327: * <p>Mutator for for cancellable property</p>
0328: *
0329: * @param cancellable
0330: */
0331: public void setCancellable(boolean cancellable) {
0332: if (configured) {
0333: throw new IllegalStateException("Configuration is frozen");
0334: }
0335:
0336: this .cancellable = cancellable;
0337: this .cancellableSet = true;
0338: }
0339:
0340: /**
0341: * <p>Returns the path of the ActionConfig that this object should inherit
0342: * properties from.</p>
0343: *
0344: * @return the path of the ActionConfig that this object should inherit
0345: * properties from.
0346: */
0347: public String getExtends() {
0348: return (this .inherit);
0349: }
0350:
0351: /**
0352: * <p>Set the path of the ActionConfig that this object should inherit
0353: * properties from.</p>
0354: *
0355: * @param inherit the path of the ActionConfig that this object should
0356: * inherit properties from.
0357: */
0358: public void setExtends(String inherit) {
0359: if (configured) {
0360: throw new IllegalStateException("Configuration is frozen");
0361: }
0362:
0363: this .inherit = inherit;
0364: }
0365:
0366: public boolean isExtensionProcessed() {
0367: return extensionProcessed;
0368: }
0369:
0370: /**
0371: * <p> Returns context-relative path of the web application resource that
0372: * will process this request.
0373: *
0374: * @return context-relative path of the web application resource that will
0375: * process this request.
0376: */
0377: public String getForward() {
0378: return (this .forward);
0379: }
0380:
0381: /**
0382: * <p> Set the context-relative path of the web application resource that
0383: * will process this request. Exactly one of <code>forward</code>,
0384: * <code>include</code>, or <code>type</code> must be specified.
0385: *
0386: * @param forward context-relative path of the web application resource
0387: * that will process this request.
0388: */
0389: public void setForward(String forward) {
0390: if (configured) {
0391: throw new IllegalStateException("Configuration is frozen");
0392: }
0393:
0394: this .forward = forward;
0395: }
0396:
0397: /**
0398: * <p> Context-relative path of the web application resource that will
0399: * process this request.
0400: *
0401: * @return Context-relative path of the web application resource that will
0402: * process this request.
0403: */
0404: public String getInclude() {
0405: return (this .include);
0406: }
0407:
0408: /**
0409: * <p> Set context-relative path of the web application resource that will
0410: * process this request. Exactly one of <code>forward</code>,
0411: * <code>include</code>, or <code>type</code> must be specified.
0412: *
0413: * @param include context-relative path of the web application resource
0414: * that will process this request.
0415: */
0416: public void setInclude(String include) {
0417: if (configured) {
0418: throw new IllegalStateException("Configuration is frozen");
0419: }
0420:
0421: this .include = include;
0422: }
0423:
0424: /**
0425: * <p> Get the context-relative path of the input form to which control
0426: * should be returned if a validation error is encountered.
0427: *
0428: * @return context-relative path of the input form to which control should
0429: * be returned if a validation error is encountered.
0430: */
0431: public String getInput() {
0432: return (this .input);
0433: }
0434:
0435: /**
0436: * <p> Set the context-relative path of the input form to which control
0437: * should be returned if a validation error is encountered. Required if
0438: * "name" is specified and the input bean returns validation errors.
0439: *
0440: * @param input context-relative path of the input form to which control
0441: * should be returned if a validation error is encountered.
0442: */
0443: public void setInput(String input) {
0444: if (configured) {
0445: throw new IllegalStateException("Configuration is frozen");
0446: }
0447:
0448: this .input = input;
0449: }
0450:
0451: /**
0452: * <p> Return the fully qualified Java class name of the
0453: * <code>MultipartRequestHandler</code> implementation class used to
0454: * process multi-part request data for this Action.
0455: */
0456: public String getMultipartClass() {
0457: return (this .multipartClass);
0458: }
0459:
0460: /**
0461: * <p> Set the fully qualified Java class name of the
0462: * <code>MultipartRequestHandler</code> implementation class used to
0463: * process multi-part request data for this Action.
0464: *
0465: * @param multipartClass fully qualified class name of the
0466: * <code>MultipartRequestHandler</code>
0467: * implementation class.
0468: */
0469: public void setMultipartClass(String multipartClass) {
0470: if (configured) {
0471: throw new IllegalStateException("Configuration is frozen");
0472: }
0473:
0474: this .multipartClass = multipartClass;
0475: }
0476:
0477: /**
0478: * <p> Return name of the form bean, if any, associated with this Action.
0479: */
0480: public String getName() {
0481: return (this .name);
0482: }
0483:
0484: /**
0485: * @param name name of the form bean associated with this Action.
0486: */
0487: public void setName(String name) {
0488: if (configured) {
0489: throw new IllegalStateException("Configuration is frozen");
0490: }
0491:
0492: this .name = name;
0493: }
0494:
0495: /**
0496: * <p> Return general purpose configuration parameter that can be used to
0497: * pass extra information to the Action instance selected by this Action.
0498: * Struts does not itself use this value in any way.
0499: */
0500: public String getParameter() {
0501: return (this .parameter);
0502: }
0503:
0504: /**
0505: * <p> General purpose configuration parameter that can be used to pass
0506: * extra information to the Action instance selected by this Action.
0507: * Struts does not itself use this value in any way.
0508: *
0509: * @param parameter General purpose configuration parameter.
0510: */
0511: public void setParameter(String parameter) {
0512: if (configured) {
0513: throw new IllegalStateException("Configuration is frozen");
0514: }
0515:
0516: this .parameter = parameter;
0517: }
0518:
0519: /**
0520: * <p> Return context-relative path of the submitted request, starting
0521: * with a slash ("/") character, and omitting any filename extension if
0522: * extension mapping is being used.
0523: */
0524: public String getPath() {
0525: return (this .path);
0526: }
0527:
0528: /**
0529: * <p> Set context-relative path of the submitted request, starting with a
0530: * slash ("/") character, and omitting any filename extension if extension
0531: * mapping is being used.
0532: *
0533: * @param path context-relative path of the submitted request.
0534: */
0535: public void setPath(String path) {
0536: if (configured) {
0537: throw new IllegalStateException("Configuration is frozen");
0538: }
0539:
0540: this .path = path;
0541: }
0542:
0543: /**
0544: * <p> Retruns prefix used to match request parameter names to form bean
0545: * property names, if any.
0546: */
0547: public String getPrefix() {
0548: return (this .prefix);
0549: }
0550:
0551: /**
0552: * @param prefix Prefix used to match request parameter names to form bean
0553: * property names, if any.
0554: */
0555: public void setPrefix(String prefix) {
0556: if (configured) {
0557: throw new IllegalStateException("Configuration is frozen");
0558: }
0559:
0560: this .prefix = prefix;
0561: }
0562:
0563: public String getRoles() {
0564: return (this .roles);
0565: }
0566:
0567: public void setRoles(String roles) {
0568: if (configured) {
0569: throw new IllegalStateException("Configuration is frozen");
0570: }
0571:
0572: this .roles = roles;
0573:
0574: if (roles == null) {
0575: roleNames = new String[0];
0576:
0577: return;
0578: }
0579:
0580: ArrayList list = new ArrayList();
0581:
0582: while (true) {
0583: int comma = roles.indexOf(',');
0584:
0585: if (comma < 0) {
0586: break;
0587: }
0588:
0589: list.add(roles.substring(0, comma).trim());
0590: roles = roles.substring(comma + 1);
0591: }
0592:
0593: roles = roles.trim();
0594:
0595: if (roles.length() > 0) {
0596: list.add(roles);
0597: }
0598:
0599: roleNames = (String[]) list.toArray(new String[list.size()]);
0600: }
0601:
0602: /**
0603: * <p> Get array of security role names used to authorize access to this
0604: * Action.
0605: */
0606: public String[] getRoleNames() {
0607: return (this .roleNames);
0608: }
0609:
0610: /**
0611: * <p> Get the scope ("request" or "session") within which our form bean
0612: * is accessed, if any.
0613: */
0614: public String getScope() {
0615: return (this .scope);
0616: }
0617:
0618: /**
0619: * @param scope scope ("request" or "session") within which our form bean
0620: * is accessed, if any.
0621: */
0622: public void setScope(String scope) {
0623: if (configured) {
0624: throw new IllegalStateException("Configuration is frozen");
0625: }
0626:
0627: this .scope = scope;
0628: }
0629:
0630: /**
0631: * <p> Return suffix used to match request parameter names to form bean
0632: * property names, if any. </p>
0633: */
0634: public String getSuffix() {
0635: return (this .suffix);
0636: }
0637:
0638: /**
0639: * @param suffix Suffix used to match request parameter names to form bean
0640: * property names, if any.
0641: */
0642: public void setSuffix(String suffix) {
0643: if (configured) {
0644: throw new IllegalStateException("Configuration is frozen");
0645: }
0646:
0647: this .suffix = suffix;
0648: }
0649:
0650: public String getType() {
0651: return (this .type);
0652: }
0653:
0654: public void setType(String type) {
0655: if (configured) {
0656: throw new IllegalStateException("Configuration is frozen");
0657: }
0658:
0659: this .type = type;
0660: }
0661:
0662: /**
0663: * <p> Determine whether Action is configured as the default one for this
0664: * module. </p>
0665: */
0666: public boolean getUnknown() {
0667: return (this .unknown);
0668: }
0669:
0670: /**
0671: * @param unknown Indicates Action is configured as the default one for
0672: * this module, when true.
0673: */
0674: public void setUnknown(boolean unknown) {
0675: if (configured) {
0676: throw new IllegalStateException("Configuration is frozen");
0677: }
0678:
0679: this .unknown = unknown;
0680: }
0681:
0682: public boolean getValidate() {
0683: return (this .validate);
0684: }
0685:
0686: public void setValidate(boolean validate) {
0687: if (configured) {
0688: throw new IllegalStateException("Configuration is frozen");
0689: }
0690:
0691: this .validate = validate;
0692: this .validateSet = true;
0693: }
0694:
0695: /**
0696: * <p> Get the name of a <code>commons-chain</code> command which should
0697: * be executed as part of the processing of this action. </p>
0698: *
0699: * @return name of a <code>commons-chain</code> command which should be
0700: * executed as part of the processing of this action.
0701: * @since Struts 1.3.0
0702: */
0703: public String getCommand() {
0704: return (this .command);
0705: }
0706:
0707: /**
0708: * <p> Get the name of a <code>commons-chain</code> catalog in which a
0709: * specified command should be sought. This is likely to be infrequently
0710: * used after a future release of <code>commons-chain</code> supports a
0711: * one-string expression of a catalog/chain combination. </p>
0712: *
0713: * @return name of a <code>commons-chain</code> catalog in which a
0714: * specified command should be sought.
0715: * @since Struts 1.3.0
0716: */
0717: public String getCatalog() {
0718: return (this .catalog);
0719: }
0720:
0721: /**
0722: * <p> Set the name of a <code>commons-chain</code> command which should
0723: * be executed as part of the processing of this action. </p>
0724: *
0725: * @param command name of a <code>commons-chain</code> command which
0726: * should be executed as part of the processing of this
0727: * action.
0728: * @since Struts 1.3.0
0729: */
0730: public void setCommand(String command) {
0731: if (configured) {
0732: throw new IllegalStateException("Configuration is frozen");
0733: }
0734:
0735: this .command = command;
0736: }
0737:
0738: /**
0739: * <p> Set the name of a <code>commons-chain</code> catalog in which a
0740: * specified command should be sought. This is likely to be infrequently
0741: * used after a future release of <code>commons-chain</code> supports a
0742: * one-string expression of a catalog/chain combination. </p>
0743: *
0744: * @param catalog name of a <code>commons-chain</code> catalog in which a
0745: * specified command should be sought.
0746: * @since Struts 1.3.0
0747: */
0748: public void setCatalog(String catalog) {
0749: if (configured) {
0750: throw new IllegalStateException("Configuration is frozen");
0751: }
0752:
0753: this .catalog = catalog;
0754: }
0755:
0756: // ------------------------------------------------------ Protected Methods
0757:
0758: /**
0759: * <p>Traces the hierarchy of this object to check if any of the ancestors
0760: * is extending this instance.</p>
0761: *
0762: * @param moduleConfig The configuration for the module being configured.
0763: * @return true if circular inheritance was detected.
0764: */
0765: protected boolean checkCircularInheritance(ModuleConfig moduleConfig) {
0766: String ancestorPath = getExtends();
0767:
0768: while (ancestorPath != null) {
0769: // check if we have the same path as an ancestor
0770: if (getPath().equals(ancestorPath)) {
0771: return true;
0772: }
0773:
0774: // get our ancestor's ancestor
0775: ActionConfig ancestor = moduleConfig
0776: .findActionConfig(ancestorPath);
0777:
0778: if (ancestor != null) {
0779: ancestorPath = ancestor.getExtends();
0780: } else {
0781: ancestorPath = null;
0782: }
0783: }
0784:
0785: return false;
0786: }
0787:
0788: /**
0789: * <p>Compare the exception handlers of this action with that of the given
0790: * and copy those that are not present.</p>
0791: *
0792: * @param baseConfig The action config to copy handlers from.
0793: * @see #inheritFrom(ActionConfig)
0794: */
0795: protected void inheritExceptionHandlers(ActionConfig baseConfig)
0796: throws ClassNotFoundException, IllegalAccessException,
0797: InstantiationException, InvocationTargetException {
0798: if (configured) {
0799: throw new IllegalStateException("Configuration is frozen");
0800: }
0801:
0802: // Inherit exception handler configs
0803: ExceptionConfig[] baseHandlers = baseConfig
0804: .findExceptionConfigs();
0805:
0806: for (int i = 0; i < baseHandlers.length; i++) {
0807: ExceptionConfig baseHandler = baseHandlers[i];
0808:
0809: // Do we have this handler?
0810: ExceptionConfig copy = this .findExceptionConfig(baseHandler
0811: .getType());
0812:
0813: if (copy == null) {
0814: // We don't have this, so let's copy it
0815: copy = (ExceptionConfig) RequestUtils
0816: .applicationInstance(baseHandler.getClass()
0817: .getName());
0818:
0819: BeanUtils.copyProperties(copy, baseHandler);
0820: this .addExceptionConfig(copy);
0821: copy.setProperties(baseHandler.copyProperties());
0822: } else {
0823: // process any extension that this config might have
0824: copy.processExtends(getModuleConfig(), this );
0825: }
0826: }
0827: }
0828:
0829: /**
0830: * <p>Compare the forwards of this action with that of the given and copy
0831: * those that are not present.</p>
0832: *
0833: * @param baseConfig The action config to copy forwards from.
0834: * @see #inheritFrom(ActionConfig)
0835: */
0836: protected void inheritForwards(ActionConfig baseConfig)
0837: throws ClassNotFoundException, IllegalAccessException,
0838: InstantiationException, InvocationTargetException {
0839: if (configured) {
0840: throw new IllegalStateException("Configuration is frozen");
0841: }
0842:
0843: // Inherit forward configs
0844: ForwardConfig[] baseForwards = baseConfig.findForwardConfigs();
0845:
0846: for (int i = 0; i < baseForwards.length; i++) {
0847: ForwardConfig baseForward = baseForwards[i];
0848:
0849: // Do we have this forward?
0850: ForwardConfig copy = this .findForwardConfig(baseForward
0851: .getName());
0852:
0853: if (copy == null) {
0854: // We don't have this, so let's copy it
0855: copy = (ForwardConfig) RequestUtils
0856: .applicationInstance(baseForward.getClass()
0857: .getName());
0858: BeanUtils.copyProperties(copy, baseForward);
0859:
0860: this .addForwardConfig(copy);
0861: copy.setProperties(baseForward.copyProperties());
0862: } else {
0863: // process any extension for this forward
0864: copy.processExtends(getModuleConfig(), this );
0865: }
0866: }
0867: }
0868:
0869: // --------------------------------------------------------- Public Methods
0870:
0871: /**
0872: * <p> Add a new <code>ExceptionConfig</code> instance to the set
0873: * associated with this action. </p>
0874: *
0875: * @param config The new configuration instance to be added
0876: * @throws IllegalStateException if this module configuration has been
0877: * frozen
0878: */
0879: public void addExceptionConfig(ExceptionConfig config) {
0880: if (configured) {
0881: throw new IllegalStateException("Configuration is frozen");
0882: }
0883:
0884: exceptions.put(config.getType(), config);
0885: }
0886:
0887: /**
0888: * <p> Add a new <code>ForwardConfig</code> instance to the set of global
0889: * forwards associated with this action. </p>
0890: *
0891: * @param config The new configuration instance to be added
0892: * @throws IllegalStateException if this module configuration has been
0893: * frozen
0894: */
0895: public void addForwardConfig(ForwardConfig config) {
0896: if (configured) {
0897: throw new IllegalStateException("Configuration is frozen");
0898: }
0899:
0900: forwards.put(config.getName(), config);
0901: }
0902:
0903: /**
0904: * <p> Return the exception configuration for the specified type, if any;
0905: * otherwise return <code>null</code>. </p>
0906: *
0907: * @param type Exception class name to find a configuration for
0908: */
0909: public ExceptionConfig findExceptionConfig(String type) {
0910: return ((ExceptionConfig) exceptions.get(type));
0911: }
0912:
0913: /**
0914: * <p> Return the exception configurations for this action. If there are
0915: * none, a zero-length array is returned. </p>
0916: */
0917: public ExceptionConfig[] findExceptionConfigs() {
0918: ExceptionConfig[] results = new ExceptionConfig[exceptions
0919: .size()];
0920:
0921: return ((ExceptionConfig[]) exceptions.values()
0922: .toArray(results));
0923: }
0924:
0925: /**
0926: * <p>Find and return the <code>ExceptionConfig</code> instance defining
0927: * how <code>Exceptions</code> of the specified type should be handled.
0928: * This is performed by checking local and then global configurations for
0929: * the specified exception's class, and then looking up the superclass
0930: * chain (again checking local and then global configurations). If no
0931: * handler configuration can be found, return <code>null</code>.</p>
0932: *
0933: * <p>Introduced in <code>ActionMapping</code> in Struts 1.1, but pushed
0934: * up to <code>ActionConfig</code> in Struts 1.2.0.</p>
0935: *
0936: * @param type Exception class for which to find a handler
0937: * @since Struts 1.2.0
0938: */
0939: public ExceptionConfig findException(Class type) {
0940: // Check through the entire superclass hierarchy as needed
0941: ExceptionConfig config;
0942:
0943: while (true) {
0944: // Check for a locally defined handler
0945: String name = type.getName();
0946:
0947: log.debug("findException: look locally for " + name);
0948: config = findExceptionConfig(name);
0949:
0950: if (config != null) {
0951: return (config);
0952: }
0953:
0954: // Check for a globally defined handler
0955: log.debug("findException: look globally for " + name);
0956: config = getModuleConfig().findExceptionConfig(name);
0957:
0958: if (config != null) {
0959: return (config);
0960: }
0961:
0962: // Loop again for our superclass (if any)
0963: type = type.getSuperclass();
0964:
0965: if (type == null) {
0966: break;
0967: }
0968: }
0969:
0970: return (null); // No handler has been configured
0971: }
0972:
0973: /**
0974: * <p> Return the forward configuration for the specified key, if any;
0975: * otherwise return <code>null</code>. </p>
0976: *
0977: * @param name Name of the forward configuration to return
0978: */
0979: public ForwardConfig findForwardConfig(String name) {
0980: return ((ForwardConfig) forwards.get(name));
0981: }
0982:
0983: /**
0984: * <p> Return all forward configurations for this module. If there are
0985: * none, a zero-length array is returned. </p>
0986: */
0987: public ForwardConfig[] findForwardConfigs() {
0988: ForwardConfig[] results = new ForwardConfig[forwards.size()];
0989:
0990: return ((ForwardConfig[]) forwards.values().toArray(results));
0991: }
0992:
0993: /**
0994: * <p> Freeze the configuration of this action. </p>
0995: */
0996: public void freeze() {
0997: super .freeze();
0998:
0999: ExceptionConfig[] econfigs = findExceptionConfigs();
1000:
1001: for (int i = 0; i < econfigs.length; i++) {
1002: econfigs[i].freeze();
1003: }
1004:
1005: ForwardConfig[] fconfigs = findForwardConfigs();
1006:
1007: for (int i = 0; i < fconfigs.length; i++) {
1008: fconfigs[i].freeze();
1009: }
1010: }
1011:
1012: /**
1013: * <p>Inherit values that have not been overridden from the provided
1014: * config object. Subclasses overriding this method should verify that
1015: * the given parameter is of a class that contains a property it is trying
1016: * to inherit:</p>
1017: *
1018: * <pre>
1019: * if (config instanceof MyCustomConfig) {
1020: * MyCustomConfig myConfig =
1021: * (MyCustomConfig) config;
1022: *
1023: * if (getMyCustomProp() == null) {
1024: * setMyCustomProp(myConfig.getMyCustomProp());
1025: * }
1026: * }
1027: * </pre>
1028: *
1029: * <p>If the given <code>config</code> is extending another object, those
1030: * extensions should be resolved before it's used as a parameter to this
1031: * method.</p>
1032: *
1033: * @param config The object that this instance will be inheriting its
1034: * values from.
1035: * @see #processExtends(ModuleConfig)
1036: */
1037: public void inheritFrom(ActionConfig config)
1038: throws ClassNotFoundException, IllegalAccessException,
1039: InstantiationException, InvocationTargetException {
1040: if (configured) {
1041: throw new IllegalStateException("Configuration is frozen");
1042: }
1043:
1044: // Inherit values that have not been overridden
1045: if (getAttribute() == null) {
1046: setAttribute(config.getAttribute());
1047: }
1048:
1049: if (!cancellableSet) {
1050: setCancellable(config.getCancellable());
1051: }
1052:
1053: if (getCatalog() == null) {
1054: setCatalog(config.getCatalog());
1055: }
1056:
1057: if (getCommand() == null) {
1058: setCommand(config.getCommand());
1059: }
1060:
1061: if (getForward() == null) {
1062: setForward(config.getForward());
1063: }
1064:
1065: if (getInclude() == null) {
1066: setInclude(config.getInclude());
1067: }
1068:
1069: if (getInput() == null) {
1070: setInput(config.getInput());
1071: }
1072:
1073: if (getMultipartClass() == null) {
1074: setMultipartClass(config.getMultipartClass());
1075: }
1076:
1077: if (getName() == null) {
1078: setName(config.getName());
1079: }
1080:
1081: if (getParameter() == null) {
1082: setParameter(config.getParameter());
1083: }
1084:
1085: if (getPath() == null) {
1086: setPath(config.getPath());
1087: }
1088:
1089: if (getPrefix() == null) {
1090: setPrefix(config.getPrefix());
1091: }
1092:
1093: if (getRoles() == null) {
1094: setRoles(config.getRoles());
1095: }
1096:
1097: if (getScope().equals("session")) {
1098: setScope(config.getScope());
1099: }
1100:
1101: if (getSuffix() == null) {
1102: setSuffix(config.getSuffix());
1103: }
1104:
1105: if (getType() == null) {
1106: setType(config.getType());
1107: }
1108:
1109: if (!getUnknown()) {
1110: setUnknown(config.getUnknown());
1111: }
1112:
1113: if (!validateSet) {
1114: setValidate(config.getValidate());
1115: }
1116:
1117: inheritExceptionHandlers(config);
1118: inheritForwards(config);
1119: inheritProperties(config);
1120: }
1121:
1122: /**
1123: * <p>Inherit configuration information from the ActionConfig that this
1124: * instance is extending. This method verifies that any action config
1125: * object that it inherits from has also had its processExtends() method
1126: * called.</p>
1127: *
1128: * @param moduleConfig The {@link ModuleConfig} that this bean is from.
1129: * @see #inheritFrom(ActionConfig)
1130: */
1131: public void processExtends(ModuleConfig moduleConfig)
1132: throws ClassNotFoundException, IllegalAccessException,
1133: InstantiationException, InvocationTargetException {
1134: if (configured) {
1135: throw new IllegalStateException("Configuration is frozen");
1136: }
1137:
1138: String ancestorPath = getExtends();
1139:
1140: if ((!extensionProcessed) && (ancestorPath != null)) {
1141: ActionConfig baseConfig = moduleConfig
1142: .findActionConfig(ancestorPath);
1143:
1144: if (baseConfig == null) {
1145: throw new NullPointerException("Unable to find "
1146: + "action for '" + ancestorPath
1147: + "' to extend.");
1148: }
1149:
1150: // Check against circular inheritance and make sure the base
1151: // config's own extends has been processed already
1152: if (checkCircularInheritance(moduleConfig)) {
1153: throw new IllegalArgumentException(
1154: "Circular inheritance detected for action "
1155: + getPath());
1156: }
1157:
1158: // Make sure the ancestor's own extension has been processed.
1159: if (!baseConfig.isExtensionProcessed()) {
1160: baseConfig.processExtends(moduleConfig);
1161: }
1162:
1163: // Copy values from the base config
1164: inheritFrom(baseConfig);
1165: }
1166:
1167: extensionProcessed = true;
1168: }
1169:
1170: /**
1171: * <p> Remove the specified exception configuration instance. </p>
1172: *
1173: * @param config ExceptionConfig instance to be removed
1174: * @throws IllegalStateException if this module configuration has been
1175: * frozen
1176: */
1177: public void removeExceptionConfig(ExceptionConfig config) {
1178: if (configured) {
1179: throw new IllegalStateException("Configuration is frozen");
1180: }
1181:
1182: exceptions.remove(config.getType());
1183: }
1184:
1185: /**
1186: * <p> Remove the specified forward configuration instance. </p>
1187: *
1188: * @param config ForwardConfig instance to be removed
1189: * @throws IllegalStateException if this module configuration has been
1190: * frozen
1191: */
1192: public void removeForwardConfig(ForwardConfig config) {
1193: if (configured) {
1194: throw new IllegalStateException("Configuration is frozen");
1195: }
1196:
1197: forwards.remove(config.getName());
1198: }
1199:
1200: /**
1201: * <p> Return a String representation of this object. </p>
1202: */
1203: public String toString() {
1204: StringBuffer sb = new StringBuffer("ActionConfig[");
1205:
1206: sb.append("cancellable=");
1207: sb.append(cancellable);
1208:
1209: sb.append(",path=");
1210: sb.append(path);
1211:
1212: sb.append(",validate=");
1213: sb.append(validate);
1214:
1215: if (actionId != null) {
1216: sb.append(",actionId=");
1217: sb.append(actionId);
1218: }
1219:
1220: if (attribute != null) {
1221: sb.append(",attribute=");
1222: sb.append(attribute);
1223: }
1224:
1225: if (catalog != null) {
1226: sb.append(",catalog=");
1227: sb.append(catalog);
1228: }
1229:
1230: if (command != null) {
1231: sb.append(",command=");
1232: sb.append(command);
1233: }
1234:
1235: if (inherit != null) {
1236: sb.append(",extends=");
1237: sb.append(inherit);
1238: }
1239:
1240: if (forward != null) {
1241: sb.append(",forward=");
1242: sb.append(forward);
1243: }
1244:
1245: if (include != null) {
1246: sb.append(",include=");
1247: sb.append(include);
1248: }
1249:
1250: if (input != null) {
1251: sb.append(",input=");
1252: sb.append(input);
1253: }
1254:
1255: if (multipartClass != null) {
1256: sb.append(",multipartClass=");
1257: sb.append(multipartClass);
1258: }
1259:
1260: if (name != null) {
1261: sb.append(",name=");
1262: sb.append(name);
1263: }
1264:
1265: if (parameter != null) {
1266: sb.append(",parameter=");
1267: sb.append(parameter);
1268: }
1269:
1270: if (prefix != null) {
1271: sb.append(",prefix=");
1272: sb.append(prefix);
1273: }
1274:
1275: if (roles != null) {
1276: sb.append(",roles=");
1277: sb.append(roles);
1278: }
1279:
1280: if (scope != null) {
1281: sb.append(",scope=");
1282: sb.append(scope);
1283: }
1284:
1285: if (suffix != null) {
1286: sb.append(",suffix=");
1287: sb.append(suffix);
1288: }
1289:
1290: if (type != null) {
1291: sb.append(",type=");
1292: sb.append(type);
1293: }
1294:
1295: return (sb.toString());
1296: }
1297: }
|