0001: /* ComplexType
0002: *
0003: * $Id: ComplexType.java 5028 2007-03-29 23:21:48Z gojomo $
0004: *
0005: * Created on Dec 17, 2003
0006: *
0007: * Copyright (C) 2004 Internet Archive.
0008: *
0009: * This file is part of the Heritrix web crawler (crawler.archive.org).
0010: *
0011: * Heritrix is free software; you can redistribute it and/or modify
0012: * it under the terms of the GNU Lesser Public License as published by
0013: * the Free Software Foundation; either version 2.1 of the License, or
0014: * any later version.
0015: *
0016: * Heritrix is distributed in the hope that it will be useful,
0017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0019: * GNU Lesser Public License for more details.
0020: *
0021: * You should have received a copy of the GNU Lesser Public License
0022: * along with Heritrix; if not, write to the Free Software
0023: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0024: */
0025: package org.archive.crawler.settings;
0026:
0027: import java.util.ArrayList;
0028: import java.util.HashMap;
0029: import java.util.Iterator;
0030: import java.util.List;
0031: import java.util.Map;
0032: import java.util.Stack;
0033: import java.util.logging.Level;
0034: import java.util.logging.Logger;
0035:
0036: import javax.management.Attribute;
0037: import javax.management.AttributeList;
0038: import javax.management.AttributeNotFoundException;
0039: import javax.management.DynamicMBean;
0040: import javax.management.InvalidAttributeValueException;
0041: import javax.management.MBeanAttributeInfo;
0042: import javax.management.MBeanException;
0043: import javax.management.MBeanInfo;
0044: import javax.management.ReflectionException;
0045:
0046: import org.apache.commons.httpclient.URIException;
0047: import org.archive.crawler.datamodel.CandidateURI;
0048: import org.archive.crawler.datamodel.CrawlOrder;
0049: import org.archive.crawler.datamodel.CrawlURI;
0050: import org.archive.crawler.settings.Constraint.FailedCheck;
0051: import org.archive.net.UURI;
0052:
0053: /** Superclass of all configurable modules.
0054: *
0055: * This class is in many ways the heart of the settings framework. All modules
0056: * that should be configurable extends this class or one of its subclasses.
0057: *
0058: * All subclasses of this class will automatically conform to the
0059: * JMX DynamicMBean. You could then use the {@link #getMBeanInfo()} method to
0060: * investigate which attributes this module supports and then use the
0061: * {@link #getAttribute(String)} and {@link #setAttribute(Attribute)} methods to
0062: * alter the attributes values.
0063: *
0064: * Because the settings framework supports per domain/host settings there is
0065: * also available context sensitive versions of the DynamicMBean methods.
0066: * If you use the non context sensitive methods, it is the global settings
0067: * that will be altered.
0068: *
0069: * @author John Erik Halse
0070: */
0071: public abstract class ComplexType extends Type implements DynamicMBean {
0072: private static Logger logger = Logger
0073: .getLogger("org.archive.crawler.settings.ComplexType");
0074:
0075: private transient SettingsHandler settingsHandler;
0076: private transient ComplexType parent;
0077: private String description;
0078: private String absoluteName;
0079: protected final List<Type> definition = new ArrayList<Type>();
0080: protected final Map<String, Type> definitionMap = new HashMap<String, Type>();
0081: private boolean initialized = false;
0082: private String[] preservedFields = new String[0];
0083:
0084: /**
0085: * Private constructor to make sure that no one
0086: * instantiates this class with the empty constructor.
0087: */
0088: private ComplexType() {
0089: super (null, null);
0090: }
0091:
0092: /** Creates a new instance of ComplexType.
0093: *
0094: * @param name the name of the element.
0095: * @param description the description of the element.
0096: */
0097: public ComplexType(String name, String description) {
0098: super (name, null);
0099: this .description = description.intern();
0100: }
0101:
0102: protected void setAsOrder(SettingsHandler settingsHandler)
0103: throws InvalidAttributeValueException {
0104: this .settingsHandler = settingsHandler;
0105: this .absoluteName = "";
0106: globalSettings().addTopLevelModule((CrawlOrder) this );
0107: addComplexType(settingsHandler.getSettingsObject(null), this );
0108: this .parent = null;
0109: }
0110:
0111: /** Get the global settings object (aka order).
0112: *
0113: * @return the global settings object.
0114: */
0115: public CrawlerSettings globalSettings() {
0116: if (settingsHandler == null) {
0117: return null;
0118: }
0119: return settingsHandler.getSettingsObject(null);
0120: }
0121:
0122: public Type addElement(CrawlerSettings settings, Type type)
0123: throws InvalidAttributeValueException {
0124: getOrCreateDataContainer(settings).addElementType(type);
0125: if (type instanceof ComplexType) {
0126: addComplexType(settings, (ComplexType) type);
0127: }
0128: return type;
0129: }
0130:
0131: private ComplexType addComplexType(CrawlerSettings settings,
0132: ComplexType object) throws InvalidAttributeValueException {
0133:
0134: if (this .settingsHandler == null) {
0135: throw new IllegalStateException(
0136: "Can't add ComplexType to 'free' ComplexType");
0137: }
0138: setupVariables(object);
0139: settings.addComplexType(object);
0140: if (!object.initialized) {
0141: Iterator it = object.definition.iterator();
0142: while (it.hasNext()) {
0143: Type t = (Type) it.next();
0144: object.addElement(settings, t);
0145: }
0146: object.earlyInitialize(settings);
0147: }
0148: object.initialized = true;
0149:
0150: return object;
0151: }
0152:
0153: private ComplexType replaceComplexType(CrawlerSettings settings,
0154: ComplexType object) throws InvalidAttributeValueException,
0155: AttributeNotFoundException {
0156: if (this .settingsHandler == null) {
0157: throw new IllegalStateException(
0158: "Can't add ComplexType to 'free' ComplexType");
0159: }
0160: String[] preservedFields = object.getPreservedFields();
0161:
0162: setupVariables(object);
0163:
0164: DataContainer oldData = settings.getData(object);
0165: settings.addComplexType(object);
0166: DataContainer newData = settings.getData(object);
0167:
0168: if (!object.initialized) {
0169: Iterator it = object.definition.iterator();
0170: while (it.hasNext()) {
0171: Type t = (Type) it.next();
0172:
0173: // Check if attribute should be copied from old object.
0174: boolean found = false;
0175: if (preservedFields.length > 0) {
0176: for (int i = 0; i < preservedFields.length; i++) {
0177: if (preservedFields[i].equals(t.getName())) {
0178: found = true;
0179: break;
0180: }
0181: }
0182: }
0183: if (found
0184: && oldData.copyAttribute(t.getName(), newData)) {
0185: if (t instanceof ComplexType) {
0186: object.setupVariables((ComplexType) t);
0187: }
0188: } else {
0189: object.addElement(settings, t);
0190: }
0191: }
0192: object.earlyInitialize(settings);
0193: }
0194: object.initialized = true;
0195:
0196: return object;
0197: }
0198:
0199: /** Set a list of attribute names that the complex type should attempt to
0200: * preserve if the module is exchanged with an other one.
0201: *
0202: * @param preservedFields array of attributenames to preserve.
0203: */
0204: protected void setPreservedFields(String[] preservedFields) {
0205: this .preservedFields = preservedFields;
0206: }
0207:
0208: /** Get a list of attribute names that the complex type should attempt to
0209: * preserve if the module is exchanged with an other one.
0210: *
0211: * @return an array of attributenames to preserve.
0212: */
0213: protected String[] getPreservedFields() {
0214: return this .preservedFields;
0215: }
0216:
0217: /** Get the active data container for this ComplexType for a specific
0218: * settings object.
0219: *
0220: * If no value has been overridden on the settings object for this
0221: * ComplexType, then it traverses up until it find a DataContainer with
0222: * values for this ComplexType.
0223: *
0224: * This method should probably not be called from user code. It is a helper
0225: * method for the settings framework.
0226: *
0227: * @param context Context from which we get settings.
0228: * @return the active DataContainer.
0229: */
0230: protected DataContainer getDataContainerRecursive(Context context) {
0231: if (context.settings == null) {
0232: return null;
0233: }
0234: DataContainer data = context.settings.getData(this );
0235: if (data == null
0236: && context.settings.getParent(context.uri) != null) {
0237: context.settings = context.settings.getParent(context.uri);
0238: data = getDataContainerRecursive(context);
0239: }
0240: return data;
0241: }
0242:
0243: /** Get the active data container for this ComplexType for a specific
0244: * settings object.
0245: *
0246: * If the key has not been overridden on the settings object for this
0247: * ComplexType, then it traverses up until it find a DataContainer with
0248: * the key for this ComplexType.
0249: *
0250: * This method should probably not be called from user code. It is a helper
0251: * method for the settings framework.
0252: *
0253: * @param context the settings object for which the {@link DataContainer}
0254: * is active.
0255: * @param key the key to look for.
0256: * @return the active DataContainer.
0257: * @throws AttributeNotFoundException
0258: */
0259: protected DataContainer getDataContainerRecursive(Context context,
0260: String key) throws AttributeNotFoundException {
0261: Context c = new Context(context.settings, context.uri);
0262: DataContainer data = getDataContainerRecursive(c);
0263: while (data != null) {
0264: if (data.containsKey(key)) {
0265: return data;
0266: }
0267: c.settings = data.getSettings().getParent(c.uri);
0268: data = getDataContainerRecursive(c);
0269: }
0270: throw new AttributeNotFoundException(key);
0271: }
0272:
0273: /** Sets up some variables for a new complex type.
0274: *
0275: * The complex type is set up to be an attribute of
0276: * this complex type.
0277: *
0278: * @param object to be set up.
0279: */
0280: private void setupVariables(ComplexType object) {
0281: object.parent = this ;
0282: object.settingsHandler = getSettingsHandler();
0283: object.absoluteName = (getAbsoluteName() + '/' + object
0284: .getName()).intern();
0285: }
0286:
0287: public SettingsHandler getSettingsHandler() {
0288: return settingsHandler;
0289: }
0290:
0291: /** Get the absolute name of this ComplexType.
0292: *
0293: * The absolute name is like a file path with the name of the element
0294: * prepended by all the parents names separated by slashes.
0295: * @return Absolute name.
0296: */
0297: public String getAbsoluteName() {
0298: return absoluteName;
0299: }
0300:
0301: /**
0302: * Get settings object valid for a URI.
0303: * <p/>
0304: * This method takes an object,
0305: * try to convert it into a {@link CrawlURI} and then tries to get the
0306: * settings object from it. If this fails, then the global settings object
0307: * is returned.
0308: * <p/>
0309: * If the requested attribute is not set on this settings
0310: * object it tries its parent until it gets a settings object where this
0311: * attribute is set is found. If nothing is found, global settings is
0312: * returned.
0313: *
0314: * @param o possible {@link CrawlURI}.
0315: * @param attributeName the attribute that should have a value set on the
0316: * returned settings object.
0317: * @return the settings object valid for the URI.
0318: */
0319: Context getSettingsFromObject(Object o, String attributeName) {
0320: Context context;
0321: if (o == null) {
0322: context = null;
0323: } else if (o instanceof Context) {
0324: context = (Context) o;
0325: } else if (o instanceof CrawlerSettings) {
0326: context = new Context((CrawlerSettings) o, null);
0327: } else if (o instanceof UURI || o instanceof CandidateURI) {
0328: // Try to get settings for URI that has no references to a
0329: // CrawlServer [SIC - CrawlURI may have CrawlServer -gjm]
0330: context = new Context();
0331: context.uri = (o instanceof CandidateURI) ? ((CandidateURI) o)
0332: .getUURI()
0333: : (UURI) o;
0334: try {
0335: context.settings = getSettingsHandler().getSettings(
0336: context.uri.getReferencedHost(), context.uri);
0337: } catch (URIException e1) {
0338: logger.severe("Failed to get host");
0339: }
0340:
0341: if (attributeName != null) {
0342: try {
0343: context.settings = getDataContainerRecursive(
0344: context, attributeName).getSettings();
0345: } catch (AttributeNotFoundException e) {
0346: // Nothing found, globals will be used
0347: }
0348: }
0349: } else {
0350: logger.warning("Unknown object type: "
0351: + o.getClass().getName());
0352: context = null;
0353: }
0354:
0355: // if settings could not be resolved use globals.
0356: if (context == null) {
0357: context = new Context(globalSettings(), null);
0358: }
0359: return context;
0360: }
0361:
0362: /** Get settings object valid for a URI.
0363: *
0364: * This method takes an object, try to convert it into a {@link CrawlURI}
0365: * and then tries to get the settings object from it. If this fails, then
0366: * the global settings object is returned.
0367: *
0368: * @param o possible {@link CrawlURI}.
0369: * @return the settings object valid for the URI.
0370: */
0371: Context getSettingsFromObject(Object o) {
0372: return getSettingsFromObject(o, null);
0373: }
0374:
0375: /** Returns true if an element is overridden for this settings object.
0376: *
0377: * @param settings the settings object to investigate.
0378: * @param name the name of the element to check.
0379: * @return true if element is overridden for this settings object, false
0380: * if not set here or is first defined here.
0381: * @throws AttributeNotFoundException if element doesn't exist.
0382: */
0383: public boolean isOverridden(CrawlerSettings settings, String name)
0384: throws AttributeNotFoundException {
0385: settings = settings == null ? globalSettings() : settings;
0386: DataContainer data = settings.getData(this );
0387: if (data == null || !data.containsKey(name)) {
0388: return false;
0389: }
0390:
0391: // Try to find attribute, will throw an exception if not found.
0392: Context context = new Context(settings.getParent(), null);
0393: getDataContainerRecursive(context, name);
0394: return true;
0395: }
0396:
0397: /** Obtain the value of a specific attribute from the crawl order.
0398: *
0399: * If the attribute doesn't exist in the crawl order, the default
0400: * value will be returned.
0401: *
0402: * @param name the name of the attribute to be retrieved.
0403: * @return The value of the attribute retrieved.
0404: * @throws AttributeNotFoundException
0405: * @throws MBeanException
0406: * @throws ReflectionException
0407: */
0408: public Object getAttribute(String name)
0409: throws AttributeNotFoundException, MBeanException,
0410: ReflectionException {
0411: return getAttribute(null, name);
0412: }
0413:
0414: /** Obtain the value of a specific attribute that is valid for a
0415: * specific CrawlURI.
0416: *
0417: * This method will try to get the attribute from the host settings
0418: * valid for the CrawlURI. If it is not found it will traverse the
0419: * settings up to the order and as a last resort deliver the default
0420: * value. This is also the case if the CrawlURI is null or if the CrawlURI
0421: * hasn't been assigned a CrawlServer.
0422: *
0423: * @param name the name of the attribute to be retrieved.
0424: * @param uri the CrawlURI that this attribute should be valid for.
0425: * @return The value of the attribute retrieved.
0426: * @see #getAttribute(Object settings, String name)
0427: * @throws AttributeNotFoundException
0428: */
0429: public Object getAttribute(String name, CrawlURI uri)
0430: throws AttributeNotFoundException {
0431: return getAttribute(uri, name);
0432: }
0433:
0434: /**
0435: * Obtain the value of a specific attribute that is valid for a specific
0436: * CrawlerSettings object.<p>
0437: *
0438: * This method will first try to get a settings object from the supplied
0439: * context, then try to look up the attribute from this settings object. If
0440: * it is not found it will traverse the settings up to the order and as a
0441: * last resort deliver the default value.
0442: *
0443: * @param context the object to get the settings from.
0444: * @param name the name of the attribute to be retrieved.
0445: * @return The value of the attribute retrieved.
0446: * @see CrawlerSettings
0447: * @throws AttributeNotFoundException
0448: */
0449: public Object getAttribute(Object context, String name)
0450: throws AttributeNotFoundException {
0451: Context ctxt = getSettingsFromObject(context);
0452:
0453: // If settings is not set, return the default value
0454: if (ctxt.settings == null) {
0455: try {
0456: return ((Type) definitionMap.get(name))
0457: .getDefaultValue();
0458: } catch (NullPointerException e) {
0459: throw new AttributeNotFoundException(
0460: "Could not find attribute: " + name);
0461: }
0462: }
0463:
0464: return getDataContainerRecursive(ctxt, name).get(name);
0465: }
0466:
0467: /**
0468: * Obtain the value of a specific attribute that is valid for a specific
0469: * CrawlerSettings object.
0470: * <p>
0471: *
0472: * This method will first try to get a settings object from the supplied
0473: * context, then try to look up the attribute from this settings object. If
0474: * it is not found it will traverse the settings up to the order and as a
0475: * last resort deliver the default value.
0476: * <p>
0477: *
0478: * The only difference from the {@link #getAttribute(Object, String)}is
0479: * that this method doesn't throw any checked exceptions. If an undefined
0480: * attribute is requested from a ComplexType, it is concidered a bug and a
0481: * runtime exception is thrown instead.
0482: *
0483: * @param context the object to get the settings from.
0484: * @param name the name of the attribute to be retrieved.
0485: * @return The value of the attribute retrieved.
0486: * @see #getAttribute(Object, String)
0487: * @see CrawlerSettings
0488: * @throws IllegalArgumentException
0489: */
0490: public Object getUncheckedAttribute(Object context, String name) {
0491: try {
0492: return getAttribute(context, name);
0493: } catch (AttributeNotFoundException e) {
0494: throw new IllegalArgumentException("Was passed '" + name
0495: + "' and got this exception: " + e);
0496: }
0497: }
0498:
0499: /** Obtain the value of a specific attribute that is valid for a
0500: * specific CrawlerSettings object.
0501: *
0502: * This method will try to get the attribute from the supplied host
0503: * settings object. If it is not found it will return <code>null</code>
0504: * and not try to investigate the hierarchy of settings.
0505: *
0506: * @param settings the CrawlerSettings object to search for this attribute.
0507: * @param name the name of the attribute to be retrieved.
0508: * @return The value of the attribute retrieved or null if its not set.
0509: * @see CrawlerSettings
0510: * @throws AttributeNotFoundException is thrown if the attribute doesn't
0511: * exist.
0512: */
0513: public Object getLocalAttribute(CrawlerSettings settings,
0514: String name) throws AttributeNotFoundException {
0515:
0516: settings = settings == null ? globalSettings() : settings;
0517:
0518: DataContainer data = settings.getData(this );
0519: if (data != null && data.containsKey(name)) {
0520: // Attribute was found return it.
0521: return data.get(name);
0522: }
0523: // Try to find the attribute, will throw an exception if not found.
0524: Context context = new Context(settings, null);
0525: getDataContainerRecursive(context, name);
0526: return null;
0527: }
0528:
0529: /** Set the value of a specific attribute of the ComplexType.
0530: *
0531: * This method sets the specific attribute for the order file.
0532: *
0533: * @param attribute The identification of the attribute to be set and the
0534: * value it is to be set to.
0535: * @throws AttributeNotFoundException is thrown if there is no attribute
0536: * with this name.
0537: * @throws InvalidAttributeValueException is thrown if the attribute is of
0538: * wrong type and cannot be converted to the right type.
0539: * @throws MBeanException this is to conform to the MBean specification, but
0540: * this exception is never thrown, though this might change in the
0541: * future.
0542: * @throws ReflectionException this is to conform to the MBean specification, but
0543: * this exception is never thrown, though this might change in the
0544: * future.
0545: * @see javax.management.DynamicMBean#setAttribute(javax.management.Attribute)
0546: */
0547: public synchronized final void setAttribute(Attribute attribute)
0548: throws AttributeNotFoundException,
0549: InvalidAttributeValueException, MBeanException,
0550: ReflectionException {
0551: setAttribute(settingsHandler.getSettingsObject(null), attribute);
0552: }
0553:
0554: /** Set the value of a specific attribute of the ComplexType.
0555: *
0556: * This method is an extension to the Dynamic MBean specification so that
0557: * it is possible to set the value for a CrawlerSettings object other than
0558: * the settings object representing the order.
0559: *
0560: * @param settings the settings object for which this attributes value is valid
0561: * @param attribute The identification of the attribute to be set and the
0562: * value it is to be set to.
0563: * @throws AttributeNotFoundException is thrown if there is no attribute
0564: * with this name.
0565: * @throws InvalidAttributeValueException is thrown if the attribute is of
0566: * wrong type and cannot be converted to the right type.
0567: * @see javax.management.DynamicMBean#setAttribute(javax.management.Attribute)
0568: */
0569: public synchronized final void setAttribute(
0570: CrawlerSettings settings, Attribute attribute)
0571: throws InvalidAttributeValueException,
0572: AttributeNotFoundException {
0573:
0574: if (settings == null) {
0575: settings = globalSettings();
0576: }
0577:
0578: DataContainer data = getOrCreateDataContainer(settings);
0579: Object value = attribute.getValue();
0580:
0581: ModuleAttributeInfo attrInfo = (ModuleAttributeInfo) getAttributeInfo(
0582: settings.getParent(), attribute.getName());
0583:
0584: ModuleAttributeInfo localAttrInfo = (ModuleAttributeInfo) data
0585: .getAttributeInfo(attribute.getName());
0586:
0587: // Check if attribute exists
0588: if (attrInfo == null && localAttrInfo == null) {
0589: throw new AttributeNotFoundException(attribute.getName());
0590: }
0591:
0592: // Check if we are overriding and if that is allowed for this attribute
0593: if (localAttrInfo == null) {
0594: if (!attrInfo.isOverrideable()) {
0595: throw new InvalidAttributeValueException(
0596: "Attribute not overrideable: "
0597: + attribute.getName());
0598: }
0599: localAttrInfo = new ModuleAttributeInfo(attrInfo);
0600: }
0601:
0602: // Check if value is of correct type. If not, see if it is
0603: // a string and try to turn it into right type
0604: Class typeClass = getDefinition(attribute.getName())
0605: .getLegalValueType();
0606: if (!(typeClass.isInstance(value)) && value instanceof String) {
0607: try {
0608: value = SettingsHandler.StringToType((String) value,
0609: SettingsHandler
0610: .getTypeName(typeClass.getName()));
0611: } catch (ClassCastException e) {
0612: throw new InvalidAttributeValueException(
0613: "Unable to decode string '" + value
0614: + "' into type '" + typeClass.getName()
0615: + "'");
0616: }
0617: }
0618:
0619: // Check if the attribute value is legal
0620: FailedCheck error = checkValue(settings, attribute.getName(),
0621: value);
0622: if (error != null) {
0623: if (error.getLevel() == Level.SEVERE) {
0624: throw new InvalidAttributeValueException(error
0625: .getMessage());
0626: } else if (error.getLevel() == Level.WARNING) {
0627: if (!getSettingsHandler().fireValueErrorHandlers(error)) {
0628: throw new InvalidAttributeValueException(error
0629: .getMessage());
0630: }
0631: } else {
0632: getSettingsHandler().fireValueErrorHandlers(error);
0633: }
0634: }
0635:
0636: // Everything ok, set it
0637: localAttrInfo.setType(value);
0638: Object oldValue = data.put(attribute.getName(), localAttrInfo,
0639: value);
0640:
0641: // If the attribute is a complex type other than the old value,
0642: // make sure that all sub attributes are correctly set
0643: if (value instanceof ComplexType && value != oldValue) {
0644: ComplexType complex = (ComplexType) value;
0645: replaceComplexType(settings, complex);
0646: }
0647: }
0648:
0649: /**
0650: * Get the content type definition for an attribute.
0651: *
0652: * @param attributeName the name of the attribute to get definition for.
0653: * @return the content type definition for the attribute.
0654: */
0655: Type getDefinition(String attributeName) {
0656: return (Type) definitionMap.get(attributeName);
0657: }
0658:
0659: /**
0660: * Check an attribute to see if it fulfills all the constraints set on the
0661: * definition of this attribute.
0662: *
0663: * @param settings the CrawlerSettings object for which this check was
0664: * executed.
0665: * @param attributeName the name of the attribute to check.
0666: * @param value the value to check.
0667: * @return null if everything is ok, otherwise it returns a FailedCheck
0668: * object with detailed information of what went wrong.
0669: */
0670: public FailedCheck checkValue(CrawlerSettings settings,
0671: String attributeName, Object value) {
0672: return checkValue(settings, attributeName,
0673: getDefinition(attributeName), value);
0674: }
0675:
0676: FailedCheck checkValue(CrawlerSettings settings,
0677: String attributeName, Type definition, Object value) {
0678: FailedCheck res = null;
0679:
0680: // Check if value fulfills any constraints
0681: List constraints = definition.getConstraints();
0682: if (constraints != null) {
0683: for (Iterator it = constraints.iterator(); it.hasNext()
0684: && res == null;) {
0685: res = ((Constraint) it.next()).check(settings, this ,
0686: definition, value);
0687: }
0688: }
0689:
0690: return res;
0691: }
0692:
0693: /** Unset an attribute on a per host level.
0694: *
0695: * This methods removes an override on a per host or per domain level.
0696: *
0697: * @param settings the settings object for which the attribute should be
0698: * unset.
0699: * @param name the name of the attribute.
0700: * @return The removed attribute or null if nothing was removed.
0701: * @throws AttributeNotFoundException is thrown if the attribute name
0702: * doesn't exist.
0703: */
0704: public Object unsetAttribute(CrawlerSettings settings, String name)
0705: throws AttributeNotFoundException {
0706:
0707: if (settings == globalSettings()) {
0708: throw new IllegalArgumentException(
0709: "Not allowed to unset attributes in Crawl Order.");
0710: }
0711:
0712: DataContainer data = settings.getData(this );
0713: if (data != null && data.containsKey(name)) {
0714: // Remove value
0715: return data.removeElement(name);
0716: }
0717:
0718: // Value not found. Check if we should return null or throw an exception
0719: // This method throws an exception if not found.
0720: Context context = new Context(settings, null);
0721: getDataContainerRecursive(context, name);
0722: return null;
0723: }
0724:
0725: private DataContainer getOrCreateDataContainer(
0726: CrawlerSettings settings)
0727: throws InvalidAttributeValueException {
0728:
0729: // Get this ComplexType's data container for the submitted settings
0730: DataContainer data = settings.getData(this );
0731:
0732: // If there isn't a container, create one
0733: if (data == null) {
0734: ComplexType parent = getParent();
0735: if (parent == null) {
0736: settings.addTopLevelModule((ModuleType) this );
0737: } else {
0738: DataContainer parentData = settings.getData(parent);
0739: if (parentData == null) {
0740: if (this instanceof ModuleType) {
0741: settings.addTopLevelModule((ModuleType) this );
0742: } else {
0743: settings.addTopLevelModule((ModuleType) parent);
0744: try {
0745: parent.setAttribute(settings, this );
0746: } catch (AttributeNotFoundException e) {
0747: logger.severe(e.getMessage());
0748: }
0749: }
0750: } else {
0751: globalSettings().getData(parent).copyAttributeInfo(
0752: getName(), parentData);
0753: }
0754: }
0755:
0756: // Create fresh DataContainer
0757: data = settings.addComplexType(this );
0758: }
0759:
0760: // Make sure that the DataContainer references right type
0761: if (data.getComplexType() != this ) {
0762: if (this instanceof ModuleType) {
0763: data = settings.addComplexType(this );
0764: }
0765: }
0766: return data;
0767: }
0768:
0769: /* (non-Javadoc)
0770: * @see javax.management.DynamicMBean#getAttributes(java.lang.String[])
0771: */
0772: public AttributeList getAttributes(String[] name) {
0773: return null;
0774: }
0775:
0776: /* (non-Javadoc)
0777: * @see javax.management.DynamicMBean#setAttributes(javax.management.AttributeList)
0778: */
0779: public AttributeList setAttributes(AttributeList attributes) {
0780: return null;
0781: }
0782:
0783: /* (non-Javadoc)
0784: * @see javax.management.DynamicMBean#invoke(java.lang.String, java.lang.Object[], java.lang.String[])
0785: */
0786: public Object invoke(String arg0, Object[] arg1, String[] arg2)
0787: throws MBeanException, ReflectionException {
0788: throw new ReflectionException(new NoSuchMethodException(
0789: "No methods to invoke."));
0790: }
0791:
0792: /* (non-Javadoc)
0793: * @see javax.management.DynamicMBean#getMBeanInfo()
0794: */
0795: public MBeanInfo getMBeanInfo() {
0796: return getMBeanInfo(globalSettings());
0797: }
0798:
0799: public MBeanInfo getMBeanInfo(Object context) {
0800: MBeanAttributeInfoIterator it = getAttributeInfoIterator(context);
0801: MBeanAttributeInfo[] attributes = new MBeanAttributeInfo[it
0802: .size()];
0803: int index = 0;
0804: while (it.hasNext()) {
0805: attributes[index++] = (MBeanAttributeInfo) it.next();
0806: }
0807:
0808: MBeanInfo info = new MBeanInfo(getClass().getName(),
0809: getDescription(), attributes, null, null, null);
0810: return info;
0811: }
0812:
0813: /** Get the effective Attribute info for an element of this type from
0814: * a settings object.
0815: *
0816: * @param settings the settings object for which the Attribute info is
0817: * effective.
0818: * @param name the name of the element to get the attribute for.
0819: * @return the attribute info
0820: */
0821: public MBeanAttributeInfo getAttributeInfo(
0822: CrawlerSettings settings, String name) {
0823:
0824: MBeanAttributeInfo info = null;
0825:
0826: Context context = new Context(settings, null);
0827: DataContainer data = getDataContainerRecursive(context);
0828: while (data != null && info == null) {
0829: info = data.getAttributeInfo(name);
0830: if (info == null) {
0831: context.settings = data.getSettings().getParent();
0832: data = getDataContainerRecursive(context);
0833: }
0834: }
0835:
0836: return info;
0837: }
0838:
0839: /** Get the Attribute info for an element of this type from the global
0840: * settings.
0841: *
0842: * @param name the name of the element to get the attribute for.
0843: * @return the attribute info
0844: */
0845: public MBeanAttributeInfo getAttributeInfo(String name) {
0846: return getAttributeInfo(globalSettings(), name);
0847: }
0848:
0849: /** Get the description of this type
0850: *
0851: * The description should be suitable for showing in a user interface.
0852: *
0853: * @return this type's description
0854: */
0855: public String getDescription() {
0856: return description;
0857: }
0858:
0859: /** Get the parent of this ComplexType.
0860: *
0861: * @return the parent of this ComplexType.
0862: */
0863: public ComplexType getParent() {
0864: return parent;
0865: }
0866:
0867: /** Set the description of this ComplexType
0868: *
0869: * The description should be suitable for showing in a user interface.
0870: *
0871: * @param string the description to set for this type.
0872: */
0873: public void setDescription(String string) {
0874: description = string;
0875: }
0876:
0877: /* (non-Javadoc)
0878: * @see org.archive.crawler.settings.Type#getDefaultValue()
0879: */
0880: public Object getDefaultValue() {
0881: return this ;
0882: }
0883:
0884: /** Add a new attribute to the definition of this ComplexType.
0885: *
0886: * This method can only be called before the ComplexType has been
0887: * initialized. This usally means that this method is available for
0888: * constructors of subclasses of this class.
0889: *
0890: * @param type the type to add.
0891: * @return the newly added type.
0892: */
0893: public Type addElementToDefinition(Type type) {
0894: if (isInitialized()) {
0895: throw new IllegalStateException(
0896: "Elements should only be added to definition in the "
0897: + "constructor.");
0898: }
0899: if (definitionMap.containsKey(type.getName())) {
0900: definition.remove(definitionMap.remove(type.getName()));
0901: }
0902:
0903: definition.add(type);
0904: definitionMap.put(type.getName(), type);
0905: return type;
0906: }
0907:
0908: /** Get an element definition from this complex type.
0909: *
0910: * This method can only be called before the ComplexType has been
0911: * initialized. This usally means that this method is available for
0912: * constructors of subclasses of this class.
0913: *
0914: * @param name name of element to get.
0915: * @return the requested element or null if non existent.
0916: */
0917: public Type getElementFromDefinition(String name) {
0918: if (isInitialized()) {
0919: throw new IllegalStateException(
0920: "Elements definition can only be accessed in the "
0921: + "constructor.");
0922: }
0923: return (Type) definitionMap.get(name);
0924: }
0925:
0926: /**
0927: * This method can only be called before the ComplexType has been
0928: * initialized. This usually means that this method is available for
0929: * constructors of subclasses of this class.
0930: * @param name Name of element to remove.
0931: * @return Element removed.
0932: */
0933: protected Type removeElementFromDefinition(final String name) {
0934: if (isInitialized()) {
0935: throw new IllegalStateException(
0936: "Elements definition can only be removed in constructor.");
0937: }
0938: Object removedObj = this .definitionMap.remove(name);
0939: if (removedObj != null) {
0940: this .definition.remove(removedObj);
0941: }
0942: return (Type) removedObj;
0943: }
0944:
0945: /** This method can be overridden in subclasses to do local
0946: * initialisation.
0947: *
0948: * This method is run before the class has been updated with
0949: * information from settings files. That implies that if you
0950: * call getAttribute inside this method you will only get the
0951: * default values.
0952: *
0953: * @param settings the CrawlerSettings object for which this
0954: * complex type is defined.
0955: */
0956: public void earlyInitialize(CrawlerSettings settings) {
0957: }
0958:
0959: /** Returns true if this ComplexType is initialized.
0960: *
0961: * @return true if this ComplexType is initialized.
0962: */
0963: public boolean isInitialized() {
0964: return initialized;
0965: }
0966:
0967: public Object[] getLegalValues() {
0968: return null;
0969: }
0970:
0971: /** Returns this object.
0972: *
0973: * This method is implemented to be able to treat the ComplexType as an
0974: * subclass of {@link javax.management.Attribute}.
0975: *
0976: * @return this object.
0977: * @see javax.management.Attribute#getValue()
0978: */
0979: public Object getValue() {
0980: return this ;
0981: }
0982:
0983: class Context {
0984: CrawlerSettings settings;
0985: UURI uri;
0986:
0987: Context() {
0988: settings = null;
0989: uri = null;
0990: }
0991:
0992: Context(CrawlerSettings settings, UURI uri) {
0993: this .settings = settings;
0994: this .uri = uri;
0995: }
0996: }
0997:
0998: /** Get an Iterator over all the attributes in this ComplexType.
0999: *
1000: * @param context the context for which this set of attributes are valid.
1001: * @return an iterator over all the attributes in this map.
1002: */
1003: public Iterator iterator(Object context) {
1004: return new AttributeIterator(context);
1005: }
1006:
1007: /** Get an Iterator over all the MBeanAttributeInfo in this ComplexType.
1008: *
1009: * @param context the context for which this set of MBeanAttributeInfo are valid.
1010: * @return an iterator over all the MBeanAttributeInfo in this map.
1011: */
1012: public MBeanAttributeInfoIterator getAttributeInfoIterator(
1013: Object context) {
1014: return new MBeanAttributeInfoIterator(context);
1015: }
1016:
1017: /**
1018: * Iterator over all attributes in a ComplexType.
1019: *
1020: * @author John Erik Halse
1021: */
1022: private class AttributeIterator implements Iterator {
1023: private Context context;
1024: private Stack<Iterator<MBeanAttributeInfo>> attributeStack = new Stack<Iterator<MBeanAttributeInfo>>();
1025: private Iterator currentIterator;
1026:
1027: public AttributeIterator(Object ctxt) {
1028: this .context = getSettingsFromObject(ctxt);
1029: Context c = new Context(context.settings, context.uri);
1030: DataContainer data = getDataContainerRecursive(c);
1031: while (data != null) {
1032: this .attributeStack.push(data
1033: .getLocalAttributeInfoList().iterator());
1034: c.settings = data.getSettings().getParent();
1035: data = getDataContainerRecursive(c);
1036: }
1037:
1038: this .currentIterator = (Iterator) this .attributeStack.pop();
1039: }
1040:
1041: public boolean hasNext() {
1042: if (this .currentIterator.hasNext()) {
1043: return true;
1044: }
1045: if (this .attributeStack.isEmpty()) {
1046: return false;
1047: }
1048: this .currentIterator = (Iterator) this .attributeStack.pop();
1049: return this .currentIterator.hasNext();
1050: }
1051:
1052: public Object next() {
1053: hasNext();
1054: try {
1055: MBeanAttributeInfo attInfo = (MBeanAttributeInfo) this .currentIterator
1056: .next();
1057: Object attr = getAttribute(this .context, attInfo
1058: .getName());
1059: if (!(attr instanceof Attribute)) {
1060: attr = new Attribute(attInfo.getName(), attr);
1061: }
1062: return attr;
1063: } catch (AttributeNotFoundException e) {
1064: // This should never happen
1065: e.printStackTrace();
1066: return null;
1067: }
1068: }
1069:
1070: public void remove() {
1071: throw new UnsupportedOperationException();
1072: }
1073: }
1074:
1075: /**
1076: * Iterator over all MBeanAttributeInfo for this ComplexType
1077: *
1078: * @author John Erik Halse
1079: */
1080: public class MBeanAttributeInfoIterator implements Iterator {
1081: private Context context;
1082: private Stack<Iterator<MBeanAttributeInfo>> attributeStack = new Stack<Iterator<MBeanAttributeInfo>>();
1083: private Iterator currentIterator;
1084: private int attributeCount = 0;
1085:
1086: public MBeanAttributeInfoIterator(Object ctxt) {
1087: this .context = getSettingsFromObject(ctxt);
1088: //Stack attributeStack = new Stack();
1089: //
1090: DataContainer data = getDataContainerRecursive(context);
1091: while (data != null) {
1092: attributeStack.push(data.getLocalAttributeInfoList()
1093: .iterator());
1094: attributeCount += data.getLocalAttributeInfoList()
1095: .size();
1096: context.settings = data.getSettings().getParent();
1097: data = getDataContainerRecursive(context);
1098: }
1099:
1100: this .currentIterator = (Iterator) this .attributeStack.pop();
1101: }
1102:
1103: public boolean hasNext() {
1104: if (this .currentIterator.hasNext()) {
1105: return true;
1106: }
1107: if (this .attributeStack.isEmpty()) {
1108: return false;
1109: }
1110: this .currentIterator = (Iterator) this .attributeStack.pop();
1111: return this .currentIterator.hasNext();
1112: }
1113:
1114: public Object next() {
1115: hasNext();
1116: MBeanAttributeInfo attInfo = (MBeanAttributeInfo) this .currentIterator
1117: .next();
1118: return attInfo;
1119: }
1120:
1121: public void remove() {
1122: throw new UnsupportedOperationException();
1123: }
1124:
1125: public int size() {
1126: return attributeCount;
1127: }
1128: }
1129:
1130: @Override
1131: public String toString() {
1132: // In 1.6, toString goes into infinite loop. Default implementation is
1133: // return getName() + '=' + getValue() but this class returns itself
1134: // for a value on which we do a toString... and around we go. Short
1135: // circuit it here.
1136: return getName() + ": " + getClass().getName() + "@"
1137: + Integer.toHexString(hashCode());
1138: }
1139: }
|