1 16 package org.apache.cocoon.components.modules.input; 17 18 import org.apache.avalon.framework.component.ComponentException; 19 import org.apache.avalon.framework.component.ComponentManager; 20 import org.apache.avalon.framework.component.Composable; 21 import org.apache.avalon.framework.configuration.Configuration; 22 import org.apache.avalon.framework.configuration.ConfigurationException; 23 import org.apache.avalon.framework.logger.Logger; 24 import org.apache.avalon.framework.thread.ThreadSafe; 25 import org.apache.cocoon.components.source.SourceUtil; 26 import org.apache.commons.collections.map.AbstractReferenceMap; 27 import org.apache.commons.collections.map.ReferenceMap; 28 import org.apache.excalibur.source.Source; 29 import org.apache.excalibur.source.SourceResolver; 30 import org.apache.excalibur.source.SourceValidity; 31 import org.w3c.dom.Document ; 32 33 import java.util.Collections ; 34 import java.util.HashMap ; 35 import java.util.Map ; 36 37 88 public class XMLFileModule extends AbstractJXPathModule 89 implements Composable, ThreadSafe { 90 91 92 String staticConfLocation; 93 94 95 Map documents; 96 97 98 boolean reloadAll; 99 100 101 boolean cacheAll; 102 103 104 boolean cacheExpressions; 105 106 107 String src; 108 109 SourceResolver resolver; 110 ComponentManager manager; 111 112 116 117 private Map expressionCache; 118 119 120 private Map expressionValuesCache; 121 122 123 126 protected static class DocumentHelper { 127 private boolean reloadable; 128 private boolean cacheable; 129 130 131 private String uri; 132 133 134 private SourceValidity validity; 135 136 137 private Document document; 138 139 140 private XMLFileModule instance; 141 142 149 public DocumentHelper(boolean reload, boolean cache, String src, XMLFileModule instance) { 150 this.reloadable = reload; 151 this.cacheable = cache; 152 this.uri = src; 153 this.instance = instance; 154 } 156 157 166 public synchronized Document getDocument(ComponentManager manager, SourceResolver resolver, Logger logger) 167 throws Exception { 168 Source src = null; 169 Document dom = null; 170 try { 171 if (this.document == null) { 172 if (logger.isDebugEnabled()) { 173 logger.debug("Document not cached... Loading uri " + this.uri); 174 } 175 src = resolver.resolveURI(this.uri); 176 this.validity = src.getValidity(); 177 this.document = SourceUtil.toDOM(src); 178 } else if (this.reloadable) { 179 if (logger.isDebugEnabled()) { 180 logger.debug("Document cached... checking validity of uri " + this.uri); 181 } 182 183 int valid = this.validity == null? SourceValidity.INVALID: this.validity.isValid(); 184 if (valid != SourceValidity.VALID) { 185 src = resolver.resolveURI(this.uri); 187 SourceValidity newValidity = src.getValidity(); 188 if (valid == SourceValidity.INVALID || this.validity.isValid(newValidity) != SourceValidity.VALID) { 190 if (logger.isDebugEnabled()) { 191 logger.debug("Reloading document... uri " + this.uri); 192 } 193 this.validity = newValidity; 194 this.document = SourceUtil.toDOM(src); 195 196 204 this.instance.flushCache(); 205 } 206 } 207 } 208 209 dom = this.document; 210 } finally { 211 if (src != null) { 212 resolver.release(src); 213 } 214 215 if (!this.cacheable) { 216 if (logger.isDebugEnabled()) { 217 logger.debug("Not caching document cached... uri " + this.uri); 218 } 219 this.validity = null; 220 this.document = null; 221 } 222 } 223 224 if (logger.isDebugEnabled()) { 225 logger.debug("Done with document... uri " + this.uri); 226 } 227 return dom; 228 } 229 } 230 231 232 236 public void compose(ComponentManager manager) throws ComponentException { 237 this.manager = manager; 238 this.resolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 239 } 240 241 259 public void configure(Configuration config) 260 throws ConfigurationException { 261 super.configure(config); 262 this.staticConfLocation = config.getLocation(); 263 this.reloadAll = config.getChild("reloadable").getValueAsBoolean(false); 264 265 if (config.getChild("cachable", false) != null) { 266 throw new ConfigurationException("Bzzt! Wrong spelling at " + 267 config.getChild("cachable").getLocation() + 268 ": please use 'cacheable', not 'cachable'"); 269 } 270 this.cacheAll = config.getChild("cacheable").getValueAsBoolean(true); 271 272 this.documents = Collections.synchronizedMap(new HashMap ()); 273 Configuration[] files = config.getChildren("file"); 274 for (int i = 0; i < files.length; i++) { 275 boolean reload = files[i].getAttributeAsBoolean("reloadable", this.reloadAll); 276 boolean cache = files[i].getAttributeAsBoolean("cacheable", this.cacheAll); 277 this.src = files[i].getAttribute("src"); 278 this.documents.put(files[i], new DocumentHelper(reload, cache, this.src, this)); 282 } 283 284 this.cacheExpressions = config.getChild("cache-expressions").getValueAsBoolean(true); 286 if (this.cacheExpressions) { 287 this.expressionCache = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT); 288 this.expressionValuesCache = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT); 289 } 290 } 291 292 295 public void dispose() { 296 super.dispose(); 297 if (this.manager != null) { 298 this.manager.release(this.resolver); 299 this.resolver = null; 300 this.manager = null; 301 } 302 303 this.documents = null; 304 this.expressionCache = null; 305 this.expressionValuesCache = null; 306 } 307 308 309 312 private DocumentHelper getDocumentHelper(Configuration modeConf) 313 throws ConfigurationException { 314 boolean hasDynamicConf = false; Configuration fileConf = null; 317 if (modeConf != null && modeConf.getChildren().length > 0) { 318 fileConf = modeConf.getChild("file", false); 319 if (fileConf == null) { 320 if (getLogger().isDebugEnabled()) { 321 getLogger().debug("Missing 'file' child element at " + modeConf.getLocation()); 322 } 323 } else { 324 hasDynamicConf = true; 325 } 326 } 327 328 String src = this.src; 329 if (hasDynamicConf) { 330 src = fileConf.getAttribute("src"); 331 } 332 333 if (src == null) { 334 throw new ConfigurationException( 335 "No source specified" 336 + (modeConf != null ? ", either dynamically in " + modeConf.getLocation() + ", or " : "") 337 + " statically in " 338 + staticConfLocation); 339 } 340 341 if (!this.documents.containsKey(src)) { 342 boolean reload = this.reloadAll; 343 boolean cache = this.cacheAll; 344 if (hasDynamicConf) { 345 reload = fileConf.getAttributeAsBoolean("reloadable", reload); 346 cache = fileConf.getAttributeAsBoolean("cacheable", cache); 347 if (fileConf.getAttribute("cachable", null) != null) { 348 throw new ConfigurationException( 349 "Bzzt! Wrong spelling at " 350 + fileConf.getLocation() 351 + ": please use 'cacheable', not 'cachable'"); 352 } 353 } 354 355 this.documents.put(src, new DocumentHelper(reload, cache, src, this)); 356 } 357 358 return (DocumentHelper) this.documents.get(src); 359 } 360 361 375 protected Object getContextObject(Configuration modeConf, Map objectModel) 376 throws ConfigurationException { 377 DocumentHelper helper = getDocumentHelper(modeConf); 378 379 try { 380 return helper.getDocument(this.manager, this.resolver, getLogger()); 381 } catch (Exception e) { 382 if (getLogger().isDebugEnabled()) { 383 getLogger().debug("Error using source " + src + "\n" + e.getMessage(), e); 384 } 385 throw new ConfigurationException("Error using source " + src, e); 386 } 387 } 388 389 public Object getAttribute(String name, Configuration modeConf, Map objectModel) 390 throws ConfigurationException { 391 return getAttribute(name, modeConf, objectModel, false); 392 } 393 394 public Object [] getAttributeValues(String name, Configuration modeConf, Map objectModel) 395 throws ConfigurationException { 396 Object result = getAttribute(name, modeConf, objectModel, true); 397 return (result != null ? (Object []) result : null); 398 } 399 400 private Object getAttribute(String name, Configuration modeConf, Map objectModel, boolean getValues) 401 throws ConfigurationException { 402 Object contextObj = getContextObject(modeConf, objectModel); 403 if (modeConf != null) { 404 name = modeConf.getChild("parameter").getValue(this.parameter != null ? this.parameter : name); 405 } 406 407 Object result = null; 408 Map cache = null; 409 boolean hasBeenCached = false; 410 if (this.cacheExpressions) { 411 cache = getExpressionCache(getValues? this.expressionValuesCache: this.expressionCache, contextObj); 412 hasBeenCached = cache.containsKey(name); 413 if (hasBeenCached) { 414 result = cache.get(name); 415 } 416 } 417 418 if (!hasBeenCached) { 419 if (getValues){ 420 result = JXPathHelper.getAttributeValues(name, modeConf, this.configuration, contextObj); 421 } else { 422 result = JXPathHelper.getAttribute(name, modeConf, this.configuration, contextObj); 423 } 424 if (this.cacheExpressions) { 425 cache.put(name, result); 426 if (this.getLogger().isDebugEnabled()) { 427 this.getLogger().debug("for " + name + " newly caching result " + result); 428 } 429 } else { 430 if (this.getLogger().isDebugEnabled()) { 431 this.getLogger().debug("for " + name + " result is " + result); 432 } 433 } 434 } else { 435 if (this.getLogger().isDebugEnabled()) { 436 this.getLogger().debug("for " + name + " using cached result " + result); 437 } 438 } 439 440 return result; 441 } 442 443 protected void flushCache() { 444 if (this.cacheExpressions) { 445 synchronized(this.expressionCache) { 446 this.expressionCache.clear(); 447 } 448 synchronized(this.expressionValuesCache) { 449 this.expressionValuesCache.clear(); 450 } 451 } 452 } 453 454 private Map getExpressionCache(Map cache, Object key) { 455 synchronized (cache) { 456 Map map = (Map) cache.get(key); 457 if (map == null) { 458 map = Collections.synchronizedMap(new HashMap ()); 459 cache.put(key, map); 460 } 461 return map; 462 } 463 } 464 } 465 | Popular Tags |