001: /**
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */package com.tc.config.schema;
004:
005: import org.apache.commons.lang.StringUtils;
006: import org.apache.xmlbeans.XmlObject;
007: import org.apache.xmlbeans.impl.values.XmlObjectBase;
008:
009: import com.tc.config.schema.dynamic.BooleanConfigItem;
010: import com.tc.config.schema.dynamic.ConfigItem;
011: import com.tc.config.schema.dynamic.ConfigItemListener;
012: import com.tc.config.schema.dynamic.FileConfigItem;
013: import com.tc.config.schema.dynamic.IntConfigItem;
014: import com.tc.config.schema.dynamic.ObjectArrayConfigItem;
015: import com.tc.config.schema.dynamic.StringArrayConfigItem;
016: import com.tc.config.schema.dynamic.StringConfigItem;
017: import com.tc.config.schema.dynamic.XPathBasedConfigItem;
018: import com.tc.logging.TCLogger;
019: import com.tc.logging.TCLogging;
020: import com.tc.util.Assert;
021:
022: import java.io.File;
023: import java.lang.reflect.Field;
024: import java.lang.reflect.InvocationHandler;
025: import java.lang.reflect.InvocationTargetException;
026: import java.lang.reflect.Method;
027: import java.lang.reflect.Modifier;
028: import java.lang.reflect.Proxy;
029: import java.util.HashMap;
030: import java.util.Map;
031:
032: /**
033: * This class exists to avoid the massive pain of writing fully-compliant test versions of every single config object in
034: * the system. This is used by the {@link com.tc.config.schema.setup.TestTVSConfigurationSetupManagerFactory}; see
035: * there for more details.
036: * </p>
037: * <p>
038: * Basically, you can use this with the {@link java.util.Proxy} stuff and any random config interface (more
039: * specifically, one that has methods that all take no parameters and return instances of {@link ConfigItem}, or
040: * subinterfaces thereof), and you'll get back a config object that's special: when you call the normal methods that
041: * return config items, you can cast the returned item to a {@link com.tc.config.schema.SettableConfigItem}, and that
042: * will let you set its value. Note that doing anything else with the returned item, like trying to get its value or add
043: * or remove listeners, will just reward you with a {@link com.tc.util.TCAssertionError}.
044: * </p>
045: * <p>
046: * This class is messy (I am in a ridiculous rush right now to get this code down). The vast majority of its code is
047: * involved in taking a root XML bean and an XPath, and then descending through child beans — creating them along
048: * the way, if necessary — to
049: */
050: public class TestConfigObjectInvocationHandler implements
051: InvocationHandler {
052:
053: private static final TCLogger logger = TCLogging
054: .getLogger(TestConfigObjectInvocationHandler.class);
055:
056: /**
057: * This is the {@link ConfigItem} implementation we return. Feel free to add interfaces to this list as we create more
058: * typed subinterfaces of {@link ConfigItem}; the methods shouldn't do anything, just throw
059: * {@link com.tc.util.TCAssertionError}s.
060: */
061: private class OurSettableConfigItem implements SettableConfigItem,
062: BooleanConfigItem, FileConfigItem, IntConfigItem,
063: ObjectArrayConfigItem, StringArrayConfigItem,
064: StringConfigItem {
065: private final String xpath;
066:
067: public OurSettableConfigItem(String xpath) {
068: this .xpath = xpath;
069: }
070:
071: public synchronized void addListener(
072: ConfigItemListener changeListener) {
073: throw Assert
074: .failure("You can only set values on these items; you shouldn't actually be using them.");
075: }
076:
077: public synchronized Object getObject() {
078: throw Assert
079: .failure("You can only set values on these items; you shouldn't actually be using them.");
080: }
081:
082: public synchronized void removeListener(
083: ConfigItemListener changeListener) {
084: throw Assert
085: .failure("You can only set values on these items; you shouldn't actually be using them.");
086: }
087:
088: public synchronized void setValue(Object newValue) {
089: for (int i = 0; i < beansToSetOn.length; ++i) {
090: logger.debug("Setting value " + newValue
091: + " on XPath '" + xpath + "' for bean "
092: + beansToSetOn[i]);
093: setValueOnBean(newValue, beansToSetOn[i], this .xpath);
094: }
095: }
096:
097: private String toMethodName(String xpathComponent) {
098: while (xpathComponent.startsWith("@"))
099: xpathComponent = xpathComponent.substring(1);
100:
101: StringBuffer out = new StringBuffer();
102: char[] in = xpathComponent.toCharArray();
103: boolean capitalizeNext = true;
104:
105: for (int i = 0; i < in.length; ++i) {
106: char ch = in[i];
107: if (ch == '-')
108: capitalizeNext = true;
109: else {
110: out.append(capitalizeNext ? Character
111: .toUpperCase(ch) : ch);
112: capitalizeNext = false;
113: }
114: }
115:
116: return out.toString();
117: }
118:
119: private void setValueOnBean(Object newValue,
120: XmlObject beanToSetOn, String theXPath) {
121: synchronized (beanToSetOn) {
122: if (StringUtils.isBlank(theXPath)) {
123: logger.debug("Setting value " + newValue
124: + " directly on bean " + beanToSetOn + ".");
125: setValueDirectly(newValue, beanToSetOn);
126: } else {
127: String remainingComponents = allButFirstComponentOf(theXPath);
128: String component = firstComponentOf(theXPath);
129:
130: if (StringUtils.isBlank(remainingComponents)) {
131: // Special case, look for set...() method or xset...() method that takes an instance of our value type
132: if (setValueAsPropertyDirectly(beanToSetOn,
133: component, newValue, "set"))
134: return;
135: if (setValueAsPropertyDirectly(beanToSetOn,
136: component, newValue, "xset"))
137: return;
138: }
139:
140: logger.debug("Looking for property '" + component
141: + "' of bean " + beanToSetOn + ".");
142: XmlObject[] children = beanToSetOn
143: .selectPath(component);
144:
145: if (children == null || children.length == 0) {
146: // Create a new one
147: logger.debug("Property '" + component
148: + "' doesn't exist; creating it.");
149: XmlObject newChild = createAndAddNewChild(
150: beanToSetOn, component);
151: children = new XmlObject[] { newChild };
152: }
153:
154: for (int i = 0; i < children.length; ++i) {
155: logger.debug("Setting value " + newValue
156: + " on child " + (i + 1) + " of "
157: + children.length + ".");
158: setValueOnBean(newValue, children[i],
159: remainingComponents);
160: }
161: }
162: }
163: }
164:
165: private XmlObject createAndAddNewChild(XmlObject beanToSetOn,
166: String name) {
167: String asPropertyName = toMethodName(name);
168: Class beanClass = beanToSetOn.getClass();
169: Method creatorMethod = null;
170: String creatorMethodName = "addNew" + asPropertyName;
171:
172: try {
173: logger
174: .debug("Trying to create new child for property '"
175: + name
176: + "' on bean "
177: + beanToSetOn
178: + " of " + beanClass + ".");
179: creatorMethod = beanClass.getMethod(creatorMethodName,
180: new Class[0]);
181: logger.debug("Found creator method " + creatorMethod);
182: Assert
183: .eval(XmlObject.class
184: .isAssignableFrom(creatorMethod
185: .getReturnType()));
186: return (XmlObject) creatorMethod.invoke(beanToSetOn,
187: new Object[0]);
188: } catch (NoSuchMethodException nsme) {
189: // leave it, try setting instead
190: logger.debug("Didn't find creator method named '"
191: + creatorMethodName + "'");
192: } catch (IllegalArgumentException iae) {
193: throw Assert.failure("Can't invoke creator method "
194: + creatorMethod, iae);
195: } catch (IllegalAccessException iae) {
196: throw Assert.failure("Can't invoke creator method "
197: + creatorMethod, iae);
198: } catch (InvocationTargetException ite) {
199: throw Assert.failure("Can't invoke creator method "
200: + creatorMethod, ite);
201: }
202:
203: Method[] allMethods = beanClass.getMethods();
204: Method xsetMethod = null;
205: String methodName = "xset" + asPropertyName;
206: String factoryClassName = null;
207:
208: try {
209: logger.debug("Looking for method named '" + methodName
210: + "' instead.");
211: for (int i = 0; i < allMethods.length; ++i) {
212: Method this Method = allMethods[i];
213: if (this Method.getName().equals(methodName)
214: && this Method.getReturnType().equals(
215: Void.TYPE)
216: && this Method.getParameterTypes().length == 1
217: && XmlObject.class
218: .isAssignableFrom(this Method
219: .getParameterTypes()[0])) {
220: if (xsetMethod != null) {
221: // formatting
222: throw Assert.failure("There are multiple '"
223: + methodName + "' methods in "
224: + beanClass + "; one is "
225: + xsetMethod + ", and another is "
226: + this Method + ".");
227: }
228: xsetMethod = this Method;
229: }
230: }
231:
232: if (xsetMethod == null) {
233: // formatting
234: throw Assert
235: .failure("There is no method named '"
236: + methodName
237: + "' in "
238: + beanClass
239: + ". If this method does exist, but on a subclass of "
240: + beanClass
241: + ", not on it itself, you may have specified the wrong XPath prefix when you created "
242: + "this invocation handler.");
243: }
244: logger.debug("Found xset...() method: " + xsetMethod);
245:
246: Class newObjectClass = xsetMethod.getParameterTypes()[0];
247: factoryClassName = newObjectClass.getName()
248: + ".Factory";
249: logger.debug("Creating a new instance of "
250: + newObjectClass + " using factory class '"
251: + factoryClassName + "'.");
252: Class factoryClass = Class.forName(factoryClassName);
253: Method createMethod = factoryClass.getMethod(
254: "newInstance", new Class[0]);
255: Assert
256: .eval(XmlObject.class
257: .isAssignableFrom(createMethod
258: .getReturnType()));
259: Assert.eval(Modifier.isStatic(createMethod
260: .getModifiers()));
261: XmlObject out = (XmlObject) createMethod.invoke(null,
262: null);
263: logger.debug("Created new object " + out + ".");
264: xsetMethod.invoke(beanToSetOn, new Object[] { out });
265: logger.debug("Set new object " + out
266: + " via xset...() method " + xsetMethod
267: + " on bean " + beanToSetOn);
268: return out;
269: } catch (ClassNotFoundException cnfe) {
270: throw Assert.failure("No factory class '"
271: + factoryClassName + "'?", cnfe);
272: } catch (IllegalArgumentException iae) {
273: throw Assert.failure(
274: "Can't create instance using factory "
275: + factoryClassName
276: + " and assign using method "
277: + xsetMethod + "?", iae);
278: } catch (IllegalAccessException iae) {
279: throw Assert.failure(
280: "Can't create instance using factory "
281: + factoryClassName
282: + " and assign using method "
283: + xsetMethod + "?", iae);
284: } catch (InvocationTargetException ite) {
285: throw Assert.failure(
286: "Can't create instance using factory "
287: + factoryClassName
288: + " and assign using method "
289: + xsetMethod + "?", ite);
290: } catch (SecurityException se) {
291: throw Assert.failure(
292: "Can't create instance using factory "
293: + factoryClassName
294: + " and assign using method "
295: + xsetMethod + "?", se);
296: } catch (NoSuchMethodException nsme) {
297: throw Assert.failure(
298: "Can't create instance using factory "
299: + factoryClassName
300: + " and assign using method "
301: + xsetMethod + "?", nsme);
302: }
303: }
304:
305: private boolean assignmentCompatible(Class parameterType,
306: Object actualObject) {
307: if (parameterType.isPrimitive()) {
308: if (parameterType.equals(Boolean.TYPE))
309: return actualObject instanceof Boolean;
310: if (parameterType.equals(Character.TYPE))
311: return actualObject instanceof Character;
312: if (parameterType.equals(Byte.TYPE))
313: return actualObject instanceof Byte;
314: if (parameterType.equals(Short.TYPE))
315: return actualObject instanceof Short;
316: if (parameterType.equals(Integer.TYPE))
317: return actualObject instanceof Integer;
318: if (parameterType.equals(Long.TYPE))
319: return actualObject instanceof Long;
320: if (parameterType.equals(Float.TYPE))
321: return actualObject instanceof Float;
322: if (parameterType.equals(Double.TYPE))
323: return actualObject instanceof Double;
324: throw Assert.failure("Unknown primitive type "
325: + parameterType);
326: } else {
327: return actualObject == null
328: || parameterType.isInstance(actualObject);
329: }
330: }
331:
332: private boolean setValueAsPropertyDirectly(XmlObject onBean,
333: String propertyName, Object newValue, String prefixToTry) {
334: Method[] allMethods = onBean.getClass().getMethods();
335: String methodName = prefixToTry
336: + toMethodName(propertyName);
337: Method setterMethod = null;
338:
339: logger.debug("Looking for '" + methodName + "' method on "
340: + onBean.getClass() + "...");
341: for (int i = 0; i < allMethods.length; ++i) {
342: if (allMethods[i].getName().equals(methodName)
343: && allMethods[i].getParameterTypes().length == 1) {
344: if (setterMethod != null) {
345: // formatting
346: logger.debug("There are multiple '"
347: + methodName + "' methods in "
348: + onBean.getClass() + "; one is "
349: + setterMethod + ", and another is "
350: + allMethods[i] + ". Returning false.");
351: return false;
352: }
353:
354: setterMethod = allMethods[i];
355: }
356: }
357:
358: if (setterMethod == null) {
359: logger.debug("Can't find '" + methodName
360: + "' method with one argument on "
361: + onBean.getClass() + "; returning false.");
362: return false;
363: }
364:
365: if (newValue == null
366: && setterMethod.getParameterTypes()[0]
367: .isPrimitive()) {
368: // formatting
369: logger
370: .debug("Unable to set null as a value on XML bean "
371: + onBean
372: + "; it requires a(n) "
373: + setterMethod.getParameterTypes()[0]
374: + ", which is a primitive type. Returning false.");
375: return false;
376: }
377:
378: if (newValue != null
379: && (!assignmentCompatible(setterMethod
380: .getParameterTypes()[0], newValue))) {
381: // formatting
382: logger.debug("Unable to set value " + newValue
383: + " on XML bean " + onBean + " via method "
384: + setterMethod + "; it requires an object of "
385: + setterMethod.getParameterTypes()[0]
386: + ", and this value is of "
387: + newValue.getClass()
388: + " instead. Returning false.");
389: return false;
390: }
391:
392: try {
393: logger.debug("Setting value " + newValue + " on "
394: + onBean + " via method " + setterMethod + ".");
395: setterMethod.invoke(onBean, new Object[] { newValue });
396: return true;
397: } catch (IllegalArgumentException iae) {
398: throw Assert.failure(
399: "Unable to invoke " + setterMethod, iae);
400: } catch (IllegalAccessException iae) {
401: throw Assert.failure(
402: "Unable to invoke " + setterMethod, iae);
403: } catch (InvocationTargetException ite) {
404: throw Assert.failure(
405: "Unable to invoke " + setterMethod, ite);
406: }
407: }
408:
409: private void setValueDirectly(Object newValue, XmlObject onBean) {
410: Method[] allMethods = onBean.getClass().getMethods();
411: Method setterMethod = null;
412: boolean isArray = newValue != null
413: && newValue.getClass().isArray();
414: String searchDescrip = isArray ? "set...Array(...[])"
415: : "set(...)";
416:
417: logger.debug("Looking for " + searchDescrip + " method on "
418: + onBean.getClass() + "...");
419: for (int i = 0; i < allMethods.length; ++i) {
420: if (isArray) {
421: if (allMethods[i].getName().startsWith("set")
422: && allMethods[i].getName()
423: .endsWith("Array")
424: && allMethods[i].getParameterTypes().length == 1
425: && allMethods[i].getParameterTypes()[0]
426: .isArray()) {
427: if (setterMethod != null) {
428: // formatting
429: throw Assert.failure("There are multiple '"
430: + searchDescrip + "' methods in "
431: + onBean.getClass() + "; one is "
432: + setterMethod
433: + ", and another is "
434: + allMethods[i]);
435: }
436:
437: setterMethod = allMethods[i];
438: }
439: } else {
440: Class declaringClass = allMethods[i]
441: .getDeclaringClass();
442: if (declaringClass.equals(XmlObject.class)
443: || declaringClass
444: .equals(XmlObjectBase.class))
445: continue;
446:
447: if (allMethods[i].getName().equals("set")
448: && allMethods[i].getParameterTypes().length == 1) {
449: if (setterMethod != null) {
450: // formatting
451: throw Assert.failure("There are multiple '"
452: + searchDescrip + "' methods in "
453: + onBean.getClass() + "; one is "
454: + setterMethod
455: + ", and another is "
456: + allMethods[i]);
457: }
458:
459: setterMethod = allMethods[i];
460: }
461: }
462: }
463:
464: if (setterMethod == null)
465: throw Assert.failure("Can't find '" + searchDescrip
466: + "' method with one argument on "
467: + onBean.getClass() + ".");
468:
469: if (newValue == null
470: && setterMethod.getParameterTypes()[0]
471: .isPrimitive()) {
472: // formatting
473: throw Assert
474: .failure("You can't set null as a value on XML bean "
475: + onBean
476: + "; it requires a(n) "
477: + setterMethod.getParameterTypes()[0]
478: + ", which is a primitive type.");
479: }
480:
481: if (newValue != null
482: && (!setterMethod.getParameterTypes()[0]
483: .isInstance(newValue))) {
484: // formatting
485: throw Assert.failure("You can't set value " + newValue
486: + " on XML bean " + onBean + " via method "
487: + setterMethod + "; it requires an object of "
488: + setterMethod.getParameterTypes()[0]
489: + ", and your value is of "
490: + newValue.getClass() + " instead.");
491: }
492:
493: try {
494: logger.debug("Setting value " + newValue + " on "
495: + onBean + " via method " + setterMethod + ".");
496: setterMethod.invoke(onBean, new Object[] { newValue });
497: } catch (IllegalArgumentException iae) {
498: throw Assert.failure(
499: "Unable to invoke " + setterMethod, iae);
500: } catch (IllegalAccessException iae) {
501: throw Assert.failure(
502: "Unable to invoke " + setterMethod, iae);
503: } catch (InvocationTargetException ite) {
504: throw Assert.failure(
505: "Unable to invoke " + setterMethod, ite);
506: }
507: }
508:
509: private String firstComponentOf(String theXPath) {
510: while (theXPath.startsWith("/"))
511: theXPath = theXPath.substring(1);
512: int index = theXPath.indexOf("/");
513: if (index < 0)
514: return theXPath;
515: Assert.eval(index > 0);
516: String out = theXPath.substring(0, index);
517: while (out.startsWith("@"))
518: out = out.substring(1);
519: return out;
520: }
521:
522: private String allButFirstComponentOf(String theXPath) {
523: while (theXPath.startsWith("/"))
524: theXPath = theXPath.substring(1);
525: int index = theXPath.indexOf("/");
526: if (index < 0)
527: return null;
528: Assert.eval(index > 0);
529: return theXPath.substring(index + 1);
530: }
531:
532: public void setValue(boolean newValue) {
533: setValue(new Boolean(newValue));
534: }
535:
536: public void setValue(int newValue) {
537: setValue(new Integer(newValue));
538: }
539:
540: public boolean getBoolean() {
541: return ((Boolean) getObject()).booleanValue();
542: }
543:
544: public String getString() {
545: return (String) getObject();
546: }
547:
548: public String[] getStringArray() {
549: return (String[]) getObject();
550: }
551:
552: public File getFile() {
553: return (File) getObject();
554: }
555:
556: public int getInt() {
557: return ((Integer) getObject()).intValue();
558: }
559:
560: public Object[] getObjects() {
561: return (Object[]) getObject();
562: }
563: }
564:
565: private final Class theInterface;
566: private final XmlObject[] beansToSetOn;
567: private final Object realImplementation;
568: private final String xpathPrefix;
569:
570: private final Map configItems;
571:
572: // The XPath prefix is because when we grab XPaths out of XPathBasedConfigItems, they're relative to the bean that
573: // that config object is based on, which might be a child of the one in the repository (using the ChildBeanRepository
574: // stuff). This path needs to be the XPath from the root of the repository to the bean used as the root of the config
575: // object.
576: public TestConfigObjectInvocationHandler(Class theInterface,
577: XmlObject[] beansToSetOn, Object realImplementation,
578: String xpathPrefix) {
579: Assert.assertNotNull(theInterface);
580: Assert.assertNoNullElements(beansToSetOn);
581: Assert.assertNotNull(realImplementation);
582:
583: Assert.eval(theInterface.isInstance(realImplementation));
584:
585: this .theInterface = theInterface;
586: this .beansToSetOn = beansToSetOn;
587: this .realImplementation = realImplementation;
588: this .xpathPrefix = xpathPrefix;
589:
590: this .configItems = new HashMap();
591: }
592:
593: public Object invoke(Object proxy, Method method, Object[] args)
594: throws Throwable {
595: Assert.assertNotNull(proxy);
596: Assert.assertNotNull(method);
597:
598: // It's a getter method
599: if (ConfigItem.class.isAssignableFrom(method.getReturnType())) {
600: String propertyName = method.getName();
601: return itemForProperty(propertyName, method);
602: } else if (NewConfig.class.isAssignableFrom(method
603: .getReturnType())) {
604: Object newRealObject = method.invoke(
605: this .realImplementation, new Object[0]);
606: String subXPath = fetchSubXPathFor(method.getName());
607: String xpath;
608: if (xpathPrefix != null && subXPath != null)
609: xpath = xpathPrefix + "/" + subXPath;
610: else if (xpathPrefix != null)
611: xpath = xpathPrefix;
612: else
613: xpath = subXPath;
614: System.err.println("Creating new sub-config object of "
615: + method.getReturnType());
616: System.err.println("XPath: " + xpath);
617: System.err.println("New real object: " + newRealObject);
618: return Proxy.newProxyInstance(getClass().getClassLoader(),
619: new Class[] { method.getReturnType() },
620: new TestConfigObjectInvocationHandler(method
621: .getReturnType(), this .beansToSetOn,
622: newRealObject, xpath));
623: } else {
624: throw Assert
625: .failure("This method, '"
626: + method.getName()
627: + "', has a return type of "
628: + method.getReturnType().getName()
629: + ", which isn't a descendant of ConfigItem. "
630: + "We don't know how to support this for test config objects yet.");
631: }
632: }
633:
634: private String fetchSubXPathFor(String methodName) {
635: String fieldName = camelToUnderscores(methodName)
636: + "_SUB_XPATH";
637:
638: try {
639: Field theField = this .realImplementation.getClass()
640: .getField(fieldName);
641: Assert.eval(Modifier.isPublic(theField.getModifiers()));
642: Assert.eval(Modifier.isStatic(theField.getModifiers()));
643: Assert.eval(Modifier.isFinal(theField.getModifiers()));
644: Assert.eval(String.class.equals(theField.getType()));
645: return (String) theField.get(null);
646: } catch (IllegalAccessException iae) {
647: throw Assert.failure("Can't fetch field '" + fieldName
648: + "'?", iae);
649: } catch (NoSuchFieldException nsfe) {
650: return null;
651: }
652: }
653:
654: private String camelToUnderscores(String methodName) {
655: StringBuffer out = new StringBuffer();
656: char[] source = methodName.toCharArray();
657: boolean lastWasLowercase = isLowercase(source[0]);
658: out.append(toUppercase(source[0]));
659:
660: for (int i = 1; i < source.length; ++i) {
661: boolean this IsLowercase = isLowercase(source[i]);
662:
663: if (lastWasLowercase && (!this IsLowercase)) {
664: // transition
665: out.append("_");
666: }
667:
668: lastWasLowercase = this IsLowercase;
669: out.append(toUppercase(source[i]));
670: }
671:
672: return out.toString();
673: }
674:
675: private boolean isLowercase(char ch) {
676: return Character.isLowerCase(ch);
677: }
678:
679: private char toUppercase(char ch) {
680: return Character.toUpperCase(ch);
681: }
682:
683: private synchronized OurSettableConfigItem itemForProperty(
684: String propertyName, Method theMethod) {
685: OurSettableConfigItem out = (OurSettableConfigItem) this .configItems
686: .get(propertyName);
687: if (out == null) {
688: try {
689: ConfigItem realItem = (ConfigItem) theMethod.invoke(
690: realImplementation, new Object[0]);
691: Assert.eval("The base item we found was a "
692: + realItem.getClass()
693: + ", not an XPathBasedConfigItem",
694: realItem instanceof XPathBasedConfigItem);
695:
696: String effectiveXPath = (this .xpathPrefix == null ? ""
697: : this .xpathPrefix + "/")
698: + ((XPathBasedConfigItem) realItem).xpath();
699: logger
700: .debug("For property '"
701: + propertyName
702: + "' on proxied config-object implementation of interface "
703: + this .theInterface.getName()
704: + ", returning a config item with effective XPath '"
705: + effectiveXPath + "'.");
706:
707: out = new OurSettableConfigItem(effectiveXPath);
708: this .configItems.put(propertyName, out);
709: } catch (IllegalAccessException iae) {
710: throw Assert
711: .failure(
712: "Unable to retrieve the real ConfigItem so we can get its XPath.",
713: iae);
714: } catch (InvocationTargetException ite) {
715: throw Assert
716: .failure(
717: "Unable to retrieve the real ConfigItem so we can get its XPath.",
718: ite);
719: }
720: }
721: return out;
722: }
723:
724: }
|