1 4 package com.tc.config.schema.dynamic; 5 6 import org.apache.xmlbeans.XmlException; 7 import org.apache.xmlbeans.XmlObject; 8 9 import com.tc.config.schema.context.ConfigContext; 10 import com.tc.config.schema.listen.ConfigurationChangeListener; 11 import com.tc.util.Assert; 12 13 import java.lang.reflect.InvocationTargetException ; 14 import java.lang.reflect.Method ; 15 16 29 public abstract class XPathBasedConfigItem implements ConfigItem, ConfigurationChangeListener { 30 31 private final ConfigContext context; 32 private final String xpath; 33 34 private Object defaultValue; 35 private boolean defaultInitialized; 36 37 private final CompoundConfigItemListener listener; 38 39 private boolean haveCurrentValue; 40 private Object currentValue; 41 42 public XPathBasedConfigItem(ConfigContext context, String xpath) { 43 Assert.assertNotNull(context); 44 Assert.assertNotBlank(xpath); 45 46 this.context = context; 47 this.xpath = xpath; 48 49 this.defaultInitialized = false; 50 51 this.listener = new CompoundConfigItemListener(); 52 53 this.haveCurrentValue = false; 54 this.currentValue = null; 55 56 this.context.itemCreated(this); 57 } 58 59 private synchronized void initializeDefaultIfNecessary() { 60 if (!this.defaultInitialized) { 61 try { 62 if (this.context.hasDefaultFor(xpath)) { 63 this.defaultValue = fetchDataFromXmlObject(this.context.defaultFor(this.xpath)); 64 } else { 65 this.defaultValue = null; 66 } 67 this.defaultInitialized = true; 68 } catch (XmlException xmle) { 69 throw Assert.failure("Couldn't use XPath '" + this.xpath + "' to fetch a default value", xmle); 70 } 71 } 72 } 73 74 80 public XPathBasedConfigItem(ConfigContext context, String xpath, Object defaultValue) { 81 Assert.assertNotNull(context); 82 Assert.assertNotBlank(xpath); 83 84 this.context = context; 85 this.xpath = xpath; 86 87 this.defaultValue = defaultValue; 88 this.defaultInitialized = true; 89 90 this.listener = new CompoundConfigItemListener(); 91 92 this.haveCurrentValue = false; 93 this.currentValue = null; 94 95 this.context.itemCreated(this); 96 } 97 98 public synchronized Object getObject() { 99 if (!this.haveCurrentValue) { 100 synchronized (this.context.syncLockForBean()) { 101 this.currentValue = fetchDataFromTopLevelBean(this.context.bean()); 102 this.haveCurrentValue = true; 103 } 104 } 105 106 return this.currentValue; 107 } 108 109 private Object fetchDataFromTopLevelBean(XmlObject bean) { 110 Object out = null; 111 112 initializeDefaultIfNecessary(); 113 114 if (bean != null) { 115 XmlObject[] targetList; 116 117 synchronized (bean) { 119 targetList = bean.selectPath(this.xpath); 120 } 121 122 if (targetList == null || targetList.length == 0 || (targetList.length == 1 && targetList[0] == null)) out = fetchDataFromXmlObject(null); 123 else if (targetList.length == 1) out = fetchDataFromXmlObject(targetList[0]); 124 else throw Assert.failure("From " + bean + ", XPath '" + this.xpath + "' selected " + targetList.length 125 + " nodes, not " + "just 1. This should never happen; there is a bug in the software."); 126 } 127 128 if (out == null) out = this.defaultValue; 129 130 return out; 131 } 132 133 protected abstract Object fetchDataFromXmlObject(XmlObject xmlObject); 134 135 protected final Object fetchDataFromXmlObjectByReflection(XmlObject xmlObject, String methodName) { 136 if (xmlObject == null) return null; 137 138 Method method = getMethodWithNoParametersByName(xmlObject.getClass(), methodName); 139 140 if (method == null) { 141 throw Assert.failure("There is no method named '" + methodName + "' on object " + xmlObject + " (of class " 143 + xmlObject.getClass().getName() + ") with no parameters."); 144 } 145 146 try { 147 return method.invoke(xmlObject, new Object [0]); 148 } catch (IllegalArgumentException iae) { 149 throw Assert.failure("Unable to invoke method " + method + ".", iae); 150 } catch (IllegalAccessException iae) { 151 throw Assert.failure("Unable to invoke method " + method + ".", iae); 152 } catch (InvocationTargetException ite) { 153 throw Assert.failure("Unable to invoke method " + method + ".", ite); 154 } 155 } 156 157 protected final Method getMethodWithNoParametersByName(Class theClass, String methodName) { 158 Method [] allMethods = theClass.getMethods(); 159 for (int i = 0; i < allMethods.length; ++i) { 160 if (allMethods[i].getName().equals(methodName) && allMethods[i].getParameterTypes().length == 0) { return allMethods[i]; } 161 } 162 163 return null; 164 } 165 166 public synchronized void addListener(ConfigItemListener changeListener) { 167 Assert.assertNotNull(changeListener); 168 this.listener.addListener(changeListener); 169 } 170 171 public synchronized void removeListener(ConfigItemListener changeListener) { 172 Assert.assertNotNull(changeListener); 173 this.listener.removeListener(changeListener); 174 } 175 176 public synchronized void configurationChanged(XmlObject oldConfig, XmlObject newConfig) { 177 Object oldValue, newValue; 178 179 synchronized (this.context.syncLockForBean()) { 180 if (this.haveCurrentValue) oldValue = this.currentValue; 181 else oldValue = fetchDataFromTopLevelBean(oldConfig); 182 183 newValue = fetchDataFromTopLevelBean(newConfig); 184 185 this.currentValue = newValue; 186 this.haveCurrentValue = true; 187 } 188 189 if (((oldValue == null) != (newValue == null)) || ((oldValue != null) && (!oldValue.equals(newValue)))) { 190 this.listener.valueChanged(oldValue, newValue); 191 } 192 } 193 194 public String toString() { 195 return "configuration item at XPath '" + this.xpath + "'"; 196 } 197 198 201 public ConfigContext context() { 202 return this.context; 203 } 204 205 208 public String xpath() { 209 return this.xpath; 210 } 211 212 215 public Object defaultValue() { 216 initializeDefaultIfNecessary(); 217 return this.defaultValue; 218 } 219 220 } 221 | Popular Tags |