Modified: ode/trunk/bpel-store/src/main/java/org/apache/ode/store/ProcessConfImpl.java URL: http://svn.apache.org/viewvc/ode/trunk/bpel-store/src/main/java/org/apache/ode/store/ProcessConfImpl.java?rev=700972&r1=700971&r2=700972&view=diff ============================================================================== --- ode/trunk/bpel-store/src/main/java/org/apache/ode/store/ProcessConfImpl.java (original) +++ ode/trunk/bpel-store/src/main/java/org/apache/ode/store/ProcessConfImpl.java Wed Oct 1 16:44:37 2008 @@ -23,6 +23,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.FileFilter; import java.net.URI; import java.util.ArrayList; import java.util.Collections; @@ -32,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Arrays; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -57,8 +59,8 @@ import org.apache.ode.store.DeploymentUnitDir.CBPInfo; import org.apache.ode.utils.DOMUtils; import org.apache.ode.utils.HierarchicalProperties; -import org.apache.ode.utils.fs.FileWatchDog; -import org.apache.ode.utils.msg.MessageBundle; +import org.apache.ode.utils.WatchDog; +import org.apache.ode.utils.CollectionUtils; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -71,9 +73,9 @@ */ public class ProcessConfImpl implements ProcessConf { private static final Log __log = LogFactory.getLog(ProcessConfImpl.class); - private static final Messages __msgs = MessageBundle.getMessages(Messages.class); private final Date _deployDate; + private File _configDir; private final Map<QName, Node> _props; private final HashMap<String, Endpoint> _partnerRoleInitialValues = new HashMap<String, Endpoint>(); @@ -91,33 +93,52 @@ // cache the inMemory flag because XMLBeans objects are heavily synchronized (guarded by a coarse-grained lock) private volatile boolean _inMemory = false; - // provide the EPR properties - private HierarchicalProperties eprProperties; + // provide the IL properties + private HierarchicalProperties ilProperties; // monitor the EPR property file and reload it if necessary - private EPRPropertiesWatchDog eprPropertiesWatchDog; - private final ReadWriteLock eprPropertiesLock = new ReentrantReadWriteLock(); + private WatchDog<Map<File, Long>, PropertiesObserver> propertiesWatchDog; + private final ReadWriteLock ilPropertiesLock = new ReentrantReadWriteLock(); private EndpointReferenceContext eprContext; ProcessConfImpl(QName pid, QName type, long version, DeploymentUnitDir du, TDeployment.Process pinfo, Date deployDate, - Map<QName, Node> props, ProcessState pstate, EndpointReferenceContext eprContext) { + Map<QName, Node> props, ProcessState pstate, EndpointReferenceContext eprContext, File configDir) { _pid = pid; _version = version; _du = du; _pinfo = pinfo; _deployDate = deployDate; + _configDir = configDir; _props = Collections.unmodifiableMap(props); _state = pstate; _type = type; _inMemory = _pinfo.isSetInMemory() && _pinfo.getInMemory(); this.eprContext = eprContext; - eprPropertiesWatchDog = new EPRPropertiesWatchDog(); + propertiesWatchDog = new WatchDog<Map<File, Long>, PropertiesObserver>(new PropertiesMutable(), new PropertiesObserver()); initLinks(); initMexInterceptors(); initEventList(); } + private List<File> collectEndpointConfigFiles() { + // please mind the order: process-level files must be before system-level files + List<File> propFiles = new ArrayList<File>(); + + propFiles.addAll(_du.getEndpointConfigFiles()); + if (_configDir != null) { + // list and sort endpoint config files + File[] files = _configDir.listFiles(new FileFilter() { + public boolean accept(File path) { + return path.getName().endsWith(".endpoint") && path.isFile(); + } + }); + Arrays.sort(files); + propFiles.addAll(Arrays.asList(files)); + } + return propFiles; + } + private void initMexInterceptors() { if (_pinfo.getMexInterceptors() != null) { for (TMexInterceptor mexInterceptor : _pinfo.getMexInterceptors().getMexInterceptorList()) { @@ -155,7 +176,7 @@ _myRoleEndpoints.put(plinkName, new Endpoint(service.getName(), service.getPort())); if (provide.isSetEnableSharing()) { - _sharedServices.add(service.getName()); + _sharedServices.add(service.getName()); } } } @@ -347,64 +368,77 @@ // update properties if necessary // do it manually to save resources (instead of using a thread) - eprPropertiesWatchDog.check(); - if (eprProperties == null) { + propertiesWatchDog.check(); + if (ilProperties == null) { return Collections.EMPTY_MAP; } else { // take a lock so we can have a consistent snapshot of the properties - eprPropertiesLock.readLock().lock(); + ilPropertiesLock.readLock().lock(); try { - return eprProperties.getProperties(service, port); + return ilProperties.getProperties(service, port); } finally { - eprPropertiesLock.readLock().unlock(); + ilPropertiesLock.readLock().unlock(); } } } - /** - * Manage the reloading of the propery file every [EMAIL PROTECTED] org.apache.ode.utils.fs.FileWatchDog#DEFAULT_DELAY}. - * The check is done manually, meaning that [EMAIL PROTECTED] #check()} must be invoked each time eprProperties is accessed. - */ - private class EPRPropertiesWatchDog extends FileWatchDog { - public EPRPropertiesWatchDog() { - super(_du.getEPRConfigFile()); + private class PropertiesMutable implements WatchDog.Mutable<Map<File, Long>> { + + public boolean exists() { + return true; + } + + public boolean hasChangedSince(Map<File, Long> since) { + return !CollectionUtils.equals(lastModified(), since); + } + + public Map<File, Long> lastModified() { + List<File> files = collectEndpointConfigFiles(); + Map<File, Long> m = new HashMap<File, Long>(files.size() * 15 / 10); + for (File f : files) m.put(f, Long.valueOf(f.lastModified())); + return m; + } } - protected void init() { - eprPropertiesLock.writeLock().lock(); - try { - if (eprProperties == null) { + private class PropertiesObserver implements WatchDog.Observer { + + public void init() { + ilPropertiesLock.writeLock().lock(); + try { try { - eprProperties = new HierarchicalProperties(super.file); + // do not hold a reference on the file list, so that changes are handled + // and always create a new instance of the HierarchicalProperties + ilProperties = new HierarchicalProperties(collectEndpointConfigFiles()); } catch (IOException e) { - throw new ContextException("EPR Config Properties cannot be loaded!", e); + throw new ContextException("Integration-Layer Properties cannot be loaded!", e); } - } else { - eprProperties.clear(); + } finally { + ilPropertiesLock.writeLock().unlock(); } - } finally { - eprPropertiesLock.writeLock().unlock(); } - } - protected boolean isInitialized() { - return eprProperties != null; - } + public boolean isInitialized() { + return ilProperties != null; + } - protected void doOnUpdate() { - eprPropertiesLock.writeLock().lock(); - try { - init(); + public void onUpdate() { + ilPropertiesLock.writeLock().lock(); try { - eprProperties.loadFile(); - } catch (IOException e) { - throw new ContextException("EPR Config Properties cannot be loaded!", e); + init(); + try { + ilProperties.loadFiles(); + } catch (IOException e) { + throw new ContextException("Integration-Layer Properties cannot be loaded!", e); + } + } finally { + ilPropertiesLock.writeLock().unlock(); } - } finally { - eprPropertiesLock.writeLock().unlock(); + } + + public void onDelete() { + init(); } } - } }
Modified: ode/trunk/bpel-store/src/main/java/org/apache/ode/store/ProcessStoreImpl.java URL: http://svn.apache.org/viewvc/ode/trunk/bpel-store/src/main/java/org/apache/ode/store/ProcessStoreImpl.java?rev=700972&r1=700971&r2=700972&view=diff ============================================================================== --- ode/trunk/bpel-store/src/main/java/org/apache/ode/store/ProcessStoreImpl.java (original) +++ ode/trunk/bpel-store/src/main/java/org/apache/ode/store/ProcessStoreImpl.java Wed Oct 1 16:44:37 2008 @@ -99,6 +99,8 @@ protected File _deployDir; + protected File _configDir; + /** * Executor used to process DB transactions. Allows us to isolate the TX context, and to ensure that only one TX gets executed a * time. We don't really care to parallelize these operations because: i) HSQL does not isolate transactions and we don't want @@ -116,22 +118,22 @@ this(null, null, "", new OdeConfigProperties(new Properties(), ""), true); } - public ProcessStoreImpl(EndpointReferenceContext eprContext, DataSource ds, String persistenceType, OdeConfigProperties props, boolean auto) { + public ProcessStoreImpl(EndpointReferenceContext eprContext, DataSource ds, String persistenceType, OdeConfigProperties props, boolean createDatamodel) { this.eprContext = eprContext; if (ds != null) { // ugly hack if (persistenceType.toLowerCase().indexOf("hib") != -1) - _cf = new org.apache.ode.store.hib.DbConfStoreConnectionFactory(ds, props.getProperties(), auto); + _cf = new org.apache.ode.store.hib.DbConfStoreConnectionFactory(ds, props.getProperties(), createDatamodel); else - _cf = new org.apache.ode.store.jpa.DbConfStoreConnectionFactory(ds, auto); + _cf = new org.apache.ode.store.jpa.DbConfStoreConnectionFactory(ds, createDatamodel); } else { // If the datasource is not provided, then we create a HSQL-based in-memory // database. Makes testing a bit simpler. DataSource hsqlds = createInternalDS(_guid); if ("hibernate".equalsIgnoreCase(persistenceType)) - _cf = new org.apache.ode.store.hib.DbConfStoreConnectionFactory(hsqlds, props.getProperties(), auto); + _cf = new org.apache.ode.store.hib.DbConfStoreConnectionFactory(hsqlds, props.getProperties(), createDatamodel); else - _cf = new org.apache.ode.store.jpa.DbConfStoreConnectionFactory(hsqlds, auto); + _cf = new org.apache.ode.store.jpa.DbConfStoreConnectionFactory(hsqlds, createDatamodel); _inMemDs = hsqlds; } @@ -219,9 +221,8 @@ throw new ContextException(errmsg); } - // final OProcess oprocess = loadCBP(cbpInfo.cbp); ProcessConfImpl pconf = new ProcessConfImpl(pid, processDD.getName(), version, du, processDD, deployDate, - calcInitialProperties(processDD), calcInitialState(processDD), eprContext); + calcInitialProperties(processDD), calcInitialState(processDD), eprContext, _configDir); processes.add(pconf); } @@ -631,7 +632,7 @@ // TODO: update the props based on the values in the DB. ProcessConfImpl pconf = new ProcessConfImpl(p.getPID(), p.getType(), p.getVersion(), dud, pinfo, dudao - .getDeployDate(), props, p.getState(), eprContext); + .getDeployDate(), props, p.getState(), eprContext, _configDir); version = p.getVersion(); _processes.put(pconf.getProcessId(), pconf); @@ -735,6 +736,14 @@ return _deployDir; } + public File getConfigDir() { + return _configDir; + } + + public void setConfigDir(File configDir) { + this._configDir = configDir; + } + public static DataSource createInternalDS(String guid) { jdbcDataSource hsqlds = new jdbcDataSource(); hsqlds.setDatabase("jdbc:hsqldb:mem:" + guid); Modified: ode/trunk/bpel-store/src/main/java/org/apache/ode/store/hib/DbConfStoreConnectionFactory.java URL: http://svn.apache.org/viewvc/ode/trunk/bpel-store/src/main/java/org/apache/ode/store/hib/DbConfStoreConnectionFactory.java?rev=700972&r1=700971&r2=700972&view=diff ============================================================================== --- ode/trunk/bpel-store/src/main/java/org/apache/ode/store/hib/DbConfStoreConnectionFactory.java (original) +++ ode/trunk/bpel-store/src/main/java/org/apache/ode/store/hib/DbConfStoreConnectionFactory.java Wed Oct 1 16:44:37 2008 @@ -78,7 +78,7 @@ final SessionFactory _sessionFactory; - public DbConfStoreConnectionFactory(DataSource ds, Properties initialProps, boolean auto) { + public DbConfStoreConnectionFactory(DataSource ds, Properties initialProps, boolean createDatamodel) { _ds = ds; // Don't want to pollute original properties @@ -102,7 +102,7 @@ } } - if (auto) { + if (createDatamodel) { properties.put(Environment.HBM2DDL_AUTO, "create-drop"); } Modified: ode/trunk/bpel-store/src/main/java/org/apache/ode/store/jpa/DbConfStoreConnectionFactory.java URL: http://svn.apache.org/viewvc/ode/trunk/bpel-store/src/main/java/org/apache/ode/store/jpa/DbConfStoreConnectionFactory.java?rev=700972&r1=700971&r2=700972&view=diff ============================================================================== --- ode/trunk/bpel-store/src/main/java/org/apache/ode/store/jpa/DbConfStoreConnectionFactory.java (original) +++ ode/trunk/bpel-store/src/main/java/org/apache/ode/store/jpa/DbConfStoreConnectionFactory.java Wed Oct 1 16:44:37 2008 @@ -35,13 +35,13 @@ private DataSource _ds; private EntityManagerFactory _emf; - public DbConfStoreConnectionFactory(DataSource ds, boolean auto) { + public DbConfStoreConnectionFactory(DataSource ds, boolean createDatamodel) { _ds = ds; HashMap propMap = new HashMap(); propMap.put("javax.persistence.nonJtaDataSource", ds); propMap.put("openjpa.Log", "log4j"); // propMap.put("openjpa.jdbc.DBDictionary", "org.apache.openjpa.jdbc.sql.DerbyDictionary"); - if (auto) propMap.put("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=false)"); + if (createDatamodel) propMap.put("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=false)"); _emf = Persistence.createEntityManagerFactory("ode-store", propMap); } Modified: ode/trunk/utils/src/main/java/org/apache/ode/utils/HierarchicalProperties.java URL: http://svn.apache.org/viewvc/ode/trunk/utils/src/main/java/org/apache/ode/utils/HierarchicalProperties.java?rev=700972&r1=700971&r2=700972&view=diff ============================================================================== --- ode/trunk/utils/src/main/java/org/apache/ode/utils/HierarchicalProperties.java (original) +++ ode/trunk/utils/src/main/java/org/apache/ode/utils/HierarchicalProperties.java Wed Oct 1 16:44:37 2008 @@ -20,6 +20,7 @@ package org.apache.ode.utils; import org.apache.commons.collections.map.MultiKeyMap; +import org.apache.commons.collections.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -27,63 +28,54 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.Set; -import java.util.Collections; -import java.util.Iterator; +import java.util.Arrays; +import java.util.List; /** - * <h3>3-level Property File</h3> - * This class loads a regular property file. The main feature is that property can - * be chained in three levels. Which are, from highest to deepest: - * <ol> - * <li>[EMAIL PROTECTED] property}: defines the default value for the given property</li> - * <li>[EMAIL PROTECTED] service-ns.service-localname.ode.property}: defines the value for all ports of the given service</li> - * <li>[EMAIL PROTECTED] service-ns.service-localname.port.ode.property}: defines the value for the given port</li> - * </ol> - * Then, properties might be queried with a service and/or port. The corresponding level will be queried for the associated value, - * if not found the level n-1 is queried, and so on until the default value. + * This class load a list of regular property files (order matters). The main feature is that property can + * be chained in three levels. Then when querying for a property, if it's not found in the deepest level, + * the parent will be queryed and so on. * <p/> - * Properties must respect the following pattern: [EMAIL PROTECTED] [service-ns.service-localname.[port.]prefix.]property} + * A prefix must be defined to discriminate the property name and the level-1, level-2 names. The default prefix is [EMAIL PROTECTED] #ODE_PREFFIX}. * <p/> - * Values may contain some environment variables. For instance, [EMAIL PROTECTED] message=You're using ${java.version}}. + * Properties must respect the following pattern: [level1.[level2.]prefix.]property * <p/> - * <h3>Namespaces Alias</h3> - * To save some typing and make sure the property is valid, namespaces might be aliased.<br/> - * To do so, add a property similar to: [EMAIL PROTECTED] alias.my-ns-nickname=http://mynamespace.org}. - * <br/>Then instead of typing [EMAIL PROTECTED] http://mynamespace.org.mylocalname.myproperty=my_value} (which is not a valid a property btw}, write: [EMAIL PROTECTED] my-ns-nickname.mylocalname.myproperty=my_value} - * + * A concrete use case could be the definition of properties for wsdl services and ports. + * <br/>Level 0 would be: values common to all services and ports. + * <br/>Level 1: values common to a given service. + * <br/>Level 2: values common to a given port. * <p/> - * <h3>Examples</h3> * For instance, if the property file looks like this: * <pre> - * alias.ex_ns=http://examples.org - * - * max-redirects=30 - * timeout=40000 + *alias.foo_ns=http://foo.com * - * ex_ns.film-service.port-of-cannes.ode.timeout=50000 - * - * ex_ns.brel-service.ode.max-redirects=40 - * ex_ns.brel-service.port-of-amsterdam.ode.max-redirects=60 + * timeout=40000 + * a_namespace_with_no_alias_defined.film-service.port-of-cannes.ode.timeout=50000 + * <p/> + * max-redirects=30 + * foo_ns.brel-service.ode.max-redirects=40 + * foo_ns.brel-service.port-of-amsterdam.ode.max-redirects=60 * </pre> * The following values may be expected: * <pre> - * getProperty("max-redirects") => 30 - * getProperty("http://examples.org", "brel-service", "max-redirects") => 40 - * getProperty("http://examples.org", "brel-service", "port-of-amsterdam", "max-redirects") => 60 - * - * getProperty("http://examples.org", "film-service", "timeout") => 40000 - * getProperty("http://examples.org", "film-service", "port-of-cannes", "timeout") => 50000 - * getProperty("http://examples.org", "brel-service", "port-of-amsterdam", "timeout") => 40000 - * - * getProperties("http://examples.org", "film-service") => Map{"timeout"=>"40000", "max-redirect"=>"30"} - * getProperties("http://examples.org", "film-service", "port-of-cannes") => Map{"timeout"=>"50000", "max-redirect"=>"30"} + * getProperty("max-redirects") => 30 + * getProperty("http://foo.com", "brel-service", "max-redirects") => 40 + * getProperty("http://foo.com", "brel-service", "port-of-amsterdam", "max-redirects") => 60 + * <p/> + * getProperty("a_namespace_with_no_alias_defined", "film-service", "timeout") => 40000 + * getProperty("a_namespace_with_no_alias_defined", "film-service", "port-of-cannes", "timeout") => 50000 + * getProperty("http://foo.com", "port-of-amsterdam", "timeout") => 40000 * </pre> * <p/> + * Values may contain some environment variables. For instance, message=You're using ${java.version}. + * <p/> * This class is not thread-safe. * * @author <a href="mailto:[EMAIL PROTECTED]">Alexis Midon</a> @@ -94,15 +86,11 @@ public static final String ODE_PREFFIX = "ode"; - // the raw properties as of loaded from the filesystem - private Properties props = new Properties(); - // map <URI, alias> - private Map<String, String> aliases = new HashMap<String, String>(); - private File file; + private File[] files; private String prefix; private String dotted_prefix; /* - This map contains ChainedMap instances chained according to the service and/or port they are associated with. + This map contains ChainedMap instances chained according to the (qualified) service and/or port they are associated with. All ChainedMap instances has a common parent. The ChainedMap instances are chained to each others so that if a property is not found for [service, port], the ChainedMap associated to [service] will be queried, and if still not found, then the common parent. @@ -119,40 +107,54 @@ private transient MultiKeyMap cacheOfImmutableMaps = new MultiKeyMap(); /** - * @param file the property file to be loaded. If the file does not exist, NO exception is thrown. Property map will be empty + * @param files the property file to be loaded. The file may not exist. + * But if the file exists it has to be a file (not a directory), otherwhise an IOException is thrown. * @param prefix the property prefix - * @throws IOException if the file exists but is a directory + * @throws IOException */ - public HierarchicalProperties(File file, String prefix) throws IOException { - this.file = file; + public HierarchicalProperties(File[] files, String prefix) throws IOException { + this.files = files; this.prefix = prefix; this.dotted_prefix = "." + prefix + "."; - loadFile(); + loadFiles(); + } + + public HierarchicalProperties(File[] files) throws IOException { + this(files, ODE_PREFFIX); + } + + public HierarchicalProperties(File file, String prefix) throws IOException { + this(new File[]{file}, prefix); } - /** - * Use [EMAIL PROTECTED] #ODE_PREFFIX} as the prefix - [EMAIL PROTECTED] #HierarchicalProperties(java.io.File, String) - */ public HierarchicalProperties(File file) throws IOException { - this(file, ODE_PREFFIX); + this(new File[]{file}, ODE_PREFFIX); + } + + public HierarchicalProperties(List<File> propFiles) throws IOException { + this(propFiles.toArray(new File[propFiles.size()]), ODE_PREFFIX); } /** - * Clear all existing content, re-read the file and parse each property. If the file does not exist, content is cleared and method returns (no exception will be thrown). - * <br/>Keep in mind that this class is not thread-safe. It's the caller's responsability to make sure that one thread is not querying some properties - * while another is re-loading the file, for instance. + * Clear all existing content, then read the file and parse each property. Simply logs a message and returns if the file does not exist. + * * @throws IOException if the file is a Directory */ - public void loadFile() throws IOException { + public void loadFiles() throws IOException { // #1. clear all existing content clear(); + // #3. put the root map + hierarchicalMap.put(null, null, new ChainedMap()); + for (File file : files) loadFile(file); + } + + protected void loadFile(File file) throws IOException { if (!file.exists()) { - if (log.isDebugEnabled()) log.debug("File does not exist [" + file + "] Properties will be empty."); + if (log.isDebugEnabled()) log.debug("File does not exist [" + file + "]"); return; } - + Properties props = new Properties(); // #2. read the file FileInputStream fis = new FileInputStream(file); try { @@ -162,64 +164,78 @@ fis.close(); } - // #3. put the root map - hierarchicalMap.put(null, null, new ChainedMap()); - - // #4. process each property + // gather all aliases + Map<String, String> nsByAlias = new HashMap<String, String>(); for (Iterator it = props.entrySet().iterator(); it.hasNext();) { Map.Entry e = (Map.Entry) it.next(); String key = (String) e.getKey(); - String value = (String) e.getValue(); + String namespace = (String) e.getValue(); + // replace any env variables by its value - value = SystemUtils.replaceSystemProperties(value); - props.put(key, value); + namespace = SystemUtils.replaceSystemProperties(namespace); + props.put(key, namespace); if (key.startsWith("alias.")) { final String alias = key.substring("alias.".length(), key.length()); - if(log.isDebugEnabled()) log.debug("Alias found: "+alias+" -> "+value); - aliases.put(value, alias); - } else { - // parse the property name - String[] info = parseProperty((String) key); - String nsalias = info[0]; - String service = info[1]; - String port = info[2]; - String targetedProperty = info[3]; - - QName qname = nsalias != null ? new QName(nsalias, service) : null; - // get the map associated to this port - ChainedMap p = (ChainedMap) hierarchicalMap.get(qname, port); - if (p == null) { - // create it if necessary - // get the associated service map - ChainedMap s = (ChainedMap) hierarchicalMap.get(qname, null); - if (s == null) { - // create the service map if necessary, the parent is the root map. - s = new ChainedMap(getRootMap()); - // put it in the multi-map - hierarchicalMap.put(qname, null, s); - } + if (log.isDebugEnabled()) log.debug("Alias found: " + alias + " -> " + namespace); + if (nsByAlias.containsKey(alias) && namespace.equals(nsByAlias.get(alias))) + throw new RuntimeException("Same alias used twice for 2 different namespaces! file=" + file + ", alias=" + alias); + nsByAlias.put(alias, namespace); + // remove the pair + it.remove(); + } + } + + // #4. process each property + + for (Iterator it = props.entrySet().iterator(); it.hasNext();) { + Map.Entry e = (Map.Entry) it.next(); + String key = (String) e.getKey(); + String value = (String) e.getValue(); - // create the map itself and link it to theservice map - p = new ChainedMap(s); + + // parse the property name + String[] info = parseProperty(key); + String nsalias = info[0]; + String service = info[1]; + String port = info[2]; + String targetedProperty = info[3]; + + QName qname = null; + if (nsalias != null) { + qname = new QName(nsByAlias.get(nsalias) != null ? nsByAlias.get(nsalias) : nsalias, service); + } + // get the map associated to this port + ChainedMap p = (ChainedMap) hierarchicalMap.get(qname, port); + if (p == null) { + // create it if necessary + // get the associated service map + ChainedMap s = (ChainedMap) hierarchicalMap.get(qname, null); + if (s == null) { + // create the service map if necessary, the parent is the root map. + s = new ChainedMap(getRootMap()); // put it in the multi-map - hierarchicalMap.put(qname, port, p); + hierarchicalMap.put(qname, null, s); } - // save the key/value in its chained map - p.put(targetedProperty, value); + // create the map itself and link it to the service map + p = new ChainedMap(s); + // put it in the multi-map + hierarchicalMap.put(qname, port, p); } + + // save the key/value in its chained map + if(log.isDebugEnabled()) log.debug("New property: "+targetedProperty+" -> "+value); + p.put(targetedProperty, value); } } /** - * Clear all content. + * Clear all content. If [EMAIL PROTECTED] #loadFiles()} is not invoked later, all returned values will be null. */ public void clear() { - props.clear(); - aliases.clear(); hierarchicalMap.clear(); cacheOfImmutableMaps.clear(); } @@ -233,9 +249,6 @@ return (ChainedMap) o; } - /** - * @see #getProperties(javax.xml.namespace.QName) - */ public Map getProperties(String serviceNamespaceURI, String serviceLocalPart) { return getProperties(new QName(serviceNamespaceURI, serviceLocalPart)); } @@ -243,40 +256,37 @@ /** * @param service * @return a map containing all the properties for the given service. - * @see #getProperties(javax.xml.namespace.QName, String) + * @see #getProperties(String, String) */ public Map getProperties(QName service) { return getProperties(service, null); } - /** - * @see #getProperties(javax.xml.namespace.QName, String) - */ public Map getProperties(String serviceNamespaceURI, String serviceLocalPart, String port) { return getProperties(new QName(serviceNamespaceURI, serviceLocalPart), port); } /** - * Return a map containing all the properties for the given service/port. The map is an immutable snapshot of the properties. - * <br/>These immutable maps are cached to avoid too many map instances. - * <br/>If [EMAIL PROTECTED] port} is null then properties defined at the service level are returned. + * Return a map containing all the properties for the given port. The map is an immutable snapshot of the properties. + * Meaning that futur changes to the properties will NOT be reflected in the returned map. + * * @param service * @param port - * @return an immutable map containing all the properties for the given port + * @return a map containing all the properties for the given port */ public Map getProperties(QName service, String port) { // no need to go further if no properties if (hierarchicalMap.isEmpty()) return Collections.EMPTY_MAP; - service = resolveNamespace(service); - // check if the cache of immutable maps contains this key + // else check the cache of ChainedMap already converted into immutable maps Map cachedMap = (Map) this.cacheOfImmutableMaps.get(service, port); if (cachedMap != null) { return cachedMap; } - // if not, get the corresponding ChainedMap and convert it into a Map + // else get the corresponding ChainedMap and convert it into a Map ChainedMap cm = (ChainedMap) hierarchicalMap.get(service, port); + // if this port is not explicitly mentioned in the multimap, get the default values. if (cm == null) { cm = (ChainedMap) hierarchicalMap.get(service, null); if (cm == null) { @@ -284,75 +294,43 @@ return getProperties((QName) null, null); } } - - // convert the ChainedMap into a Map and cache it Map snapshotMap = new HashMap(cm.size() * 15 / 10); for (Object key : cm.keySet()) { snapshotMap.put(key, cm.get(key)); } snapshotMap = Collections.unmodifiableMap(snapshotMap); - // put it in cache to avoid creating one map on each invocation + // put it in cache to avoid creating one map at each invocation this.cacheOfImmutableMaps.put(service, port, snapshotMap); return snapshotMap; } - /** - * - * @param property the property to be queried - * @return the default value for this property - */ public String getProperty(String property) { return (String) getRootMap().get(property); } - /** - * @see #getProperty(javax.xml.namespace.QName, String) - */ public String getProperty(String serviceNamespaceURI, String serviceLocalPart, String property) { return getProperty(new QName(serviceNamespaceURI, serviceLocalPart), property); } - /** - * @return the value associated to this property for the given service - */ public String getProperty(QName service, String property) { return getProperty(service, null, property); } - /** - * @see #getProperty(javax.xml.namespace.QName, String, String) - */ public String getProperty(String serviceNamespaceURI, String serviceLocalPart, String port, String property) { return getProperty(new QName(serviceNamespaceURI, serviceLocalPart), port, property); } - /** - * Equivalent [EMAIL PROTECTED] getProperties(service,port).get(property)} - * @return the value associated to this property for the given service/port - */ public String getProperty(QName service, String port, String property) { - return (String) getProperties(service,port).get(property); + return (String) getProperties(service, port).get(property); } - /** - * Resolved the service qname associated to the given aliased service name. - * <p/>For instance, [EMAIL PROTECTED] resolveAlias(new QName("my-ns", "a-name"))} will return - * [EMAIL PROTECTED] new QName("http://examples.com", "a-name")} if the alias [EMAIL PROTECTED] my-ns => http://examples.com} exists. - * @param aliasedServiceName a service name using an alias - * @return the qname of the service associated to this alias if defined in the alias map, or aliasedServiceName itself. - */ - private QName resolveNamespace(QName aliasedServiceName) { - if (aliasedServiceName != null && aliases.containsKey(aliasedServiceName.getNamespaceURI())) { - return new QName(aliases.get(aliasedServiceName.getNamespaceURI()), aliasedServiceName.getLocalPart()); - } - return aliasedServiceName; + public String getPrefix() { + return prefix; } - /** - * @return an array of strings containing: namespace alias, service, port, targeted property - */ + private String[] parseProperty(String property) { - // namespace alias, service, port, targeted property + // aliaas ns, service, port, targeted property String[] res = new String[4]; int index = property.indexOf(dotted_prefix); Modified: ode/trunk/utils/src/main/java/org/apache/ode/utils/WatchDog.java URL: http://svn.apache.org/viewvc/ode/trunk/utils/src/main/java/org/apache/ode/utils/WatchDog.java?rev=700972&r1=700971&r2=700972&view=diff ============================================================================== --- ode/trunk/utils/src/main/java/org/apache/ode/utils/WatchDog.java (original) +++ ode/trunk/utils/src/main/java/org/apache/ode/utils/WatchDog.java Wed Oct 1 16:44:37 2008 @@ -22,6 +22,12 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import java.io.File; +import java.util.Map; +import java.util.HashMap; +import java.util.Collection; +import java.util.List; + /** * This class is based on [EMAIL PROTECTED] org.apache.log4j.helpers.FileWatchdog}.<p/> * Modifications have been made to support additional abstract ressource and more events (creation, deletion and updates), and to allow "manual" @@ -36,15 +42,19 @@ * * @author <a href="mailto:[EMAIL PROTECTED]">Alexis Midon</a> */ -public class WatchDog<T> implements Runnable { +public class WatchDog<T, C extends WatchDog.Observer> implements Runnable { static final public long DEFAULT_DELAY = 30000; final Log log = LogFactory.getLog(WatchDog.class); private long expire; private T lastModif; - private long delay; + private long delay = DEFAULT_DELAY; private boolean existedBefore, warnedAlready, interrupted; - protected final Mutable<T> mutable; + protected Mutable<T> mutable; + protected C observer; + + public WatchDog() { + } /** * @param mutable the object to watch closely @@ -55,45 +65,25 @@ this.delay = delay; } + public WatchDog(Mutable<T> mutable, C observer) { + this.mutable = mutable; + this.observer = observer; + } + /** * @see #WatchDog(org.apache.ode.utils.WatchDog.Mutable, long) */ public WatchDog(Mutable<T> mutable) { this.mutable = mutable; - this.delay = DEFAULT_DELAY; - } - - protected boolean isInitialized() { - return true; } - /** - * Called by [EMAIL PROTECTED] #check()} if the object is not [EMAIL PROTECTED] #isInitialized initialized} and the [EMAIL PROTECTED] WatchDog.Mutable#exists()} resource does not exist}. - * <br/> This method might called to reset the object. - * - * @throws Exception - */ - protected void init() { - } - /** - * Called only if the resource previously existed and now does not exist. - * <br/>The default implementation invokes [EMAIL PROTECTED] #init()} . - * - * @throws Exception - */ - protected void doOnDelete() { - init(); + public Mutable<T> getMutable() { + return mutable; } - /** - * Called only if the resource previously existed but the [EMAIL PROTECTED] WatchDog.Mutable#lastModified()} timestamp has changed (greater than the previous value). - * <br/>The default implementation invokes [EMAIL PROTECTED] #init()} . - * - * @throws Exception - */ - protected void doOnUpdate() { - init(); + public C getObserver() { + return observer; } public long getDelay() { @@ -123,24 +113,24 @@ long now = System.currentTimeMillis(); if (expire <= now) { expire = now + delay; - if (log.isDebugEnabled()) log.debug("Check for changes: "+mutable); + if (log.isDebugEnabled()) log.debug("Check for changes: " + mutable); if (mutable.exists()) { existedBefore = true; - if (lastModif==null || mutable.hasChangedSince(lastModif)) { + if (lastModif == null || mutable.hasChangedSince(lastModif)) { lastModif = mutable.lastModified(); - doOnUpdate(); + observer.onUpdate(); if (log.isInfoEnabled()) log.info(mutable + " updated"); warnedAlready = false; } - } else if (!isInitialized()) { + } else if (!observer.isInitialized()) { // no resource and first time - init(); + observer.init(); if (log.isInfoEnabled()) log.info(mutable + " initialized"); } else { if (existedBefore) { existedBefore = false; lastModif = null; - doOnDelete(); + observer.onDelete(); if (log.isInfoEnabled()) log.info(mutable + " deleted"); } if (!warnedAlready) { @@ -151,6 +141,14 @@ } } + public static <C extends Observer> WatchDog<Long, C> watchFile(File file, C handler) { + return new WatchDog<Long, C>(new FileMutable(file), handler); + } + + public static <C extends Observer> WatchDog<Map<File, Long>, C> watchFiles(List<File> files, C handler) { + return new WatchDog<Map<File, Long>, C>(new FileSetMutable(files), handler); + } + /** * have you said that duck typing would be nice? */ @@ -162,4 +160,123 @@ T lastModified(); } + static public class FileMutable implements WatchDog.Mutable<Long> { + File file; + + public FileMutable(File file) { + this.file = file; + } + + public boolean exists() { + return file.exists(); + } + + public boolean hasChangedSince(Long since) { + // do use 'greater than' to handle file deletion. The timestamp of a non-exising file is 0L. + return lastModified().longValue() != since.longValue(); + } + + public Long lastModified() { + return Long.valueOf(file.lastModified()); + } + + public String toString() { + return file.toString(); + } + } + + static public class FileSetMutable implements WatchDog.Mutable<Map<File, Long>> { + + File[] files; + + public FileSetMutable(Collection<File> files) { + this.files = new File[files.size()]; + files.toArray(this.files); + } + + public FileSetMutable(File[] files) { + this.files = files; + } + + public boolean exists() { + return true; + } + + public boolean hasChangedSince(Map<File, Long> since) { + Map<File, Long> snapshot = lastModified(); + return !CollectionUtils.equals(snapshot, since); + } + + public Map<File, Long> lastModified() { + Map<File, Long> m = new HashMap<File, Long>(files.length * 15 / 10); + for (File f : files) m.put(f, Long.valueOf(f.lastModified())); + return m; + } + } + + public interface Observer { + + boolean isInitialized(); + + + /** + * Called by [EMAIL PROTECTED] WatchDog#check()} if the underlying object is not [EMAIL PROTECTED] #isInitialized initialized} and the [EMAIL PROTECTED] WatchDog.Mutable#exists()} resource does not exist}. + * <br/> This method might called to reset the underlying object. + * + * @throws Exception + */ + void init(); + + /** + * Called only if the resource previously existed and now does not exist. + * <br/>The default implementation invokes [EMAIL PROTECTED] #init()} . + * + * @throws Exception + */ + void onDelete(); + + /** + * Called only if the resource previously existed but the [EMAIL PROTECTED] WatchDog.Mutable#lastModified()} timestamp has changed (greater than the previous value). + * <br/>The default implementation invokes [EMAIL PROTECTED] #init()} . + * + * @throws Exception + */ + void onUpdate(); + + } + + /** + * A default implementation of #ChangeHandler. Delete and Update will both invoke the #init method which satifies most use cases. + * So subclasses may simply override the #init method to fit their own needs. + */ + public static class DefaultObserver implements Observer { + + /** + * @return true + */ + public boolean isInitialized() { + return true; + } + + /** + * empty implementation + */ + public void init() { + } + + /** + * delegate to #init + */ + public void onDelete() { + init(); + } + + /** + * delegate to #init + */ + public void onUpdate() { + init(); + } + + } } Modified: ode/trunk/utils/src/test/java/org/apache/ode/utils/HierarchicalPropertiesTest.java URL: http://svn.apache.org/viewvc/ode/trunk/utils/src/test/java/org/apache/ode/utils/HierarchicalPropertiesTest.java?rev=700972&r1=700971&r2=700972&view=diff ============================================================================== --- ode/trunk/utils/src/test/java/org/apache/ode/utils/HierarchicalPropertiesTest.java (original) +++ ode/trunk/utils/src/test/java/org/apache/ode/utils/HierarchicalPropertiesTest.java Wed Oct 1 16:44:37 2008 @@ -23,9 +23,9 @@ import java.io.File; import java.io.IOException; -import java.util.Map; -import java.util.List; import java.util.Arrays; +import java.util.List; +import java.util.Map; /** * @author <a href="mailto:[EMAIL PROTECTED]">Alexis Midon</a> @@ -34,37 +34,27 @@ protected HierarchicalProperties hp; protected void setUp() throws Exception { - File file = new File(getClass().getResource("/hierarchical.properties").toURI()); - hp = new HierarchicalProperties(file); + File[] files = new File[]{new File(getClass().getResource("/hierarchical-2.properties").toURI()), + new File(getClass().getResource("/hierarchical-1.properties").toURI())}; + hp = new HierarchicalProperties(files); } - public void testGetPropertyUsingAliases() { + public void testGetProperty() { String msg = "Returned value does not match expected value for this property!"; - assertEquals(msg, hp.getProperty("max-redirects"), "30"); - assertEquals(msg, hp.getProperty("bar", "brel-service", "max-redirects"), "40"); - assertEquals(msg, hp.getProperty("bar", "brel-service", "port-of-amsterdam", "max-redirects"), "60"); - assertEquals(msg, hp.getProperty("bar", "brel-service", "port-of-amsterdam", "timeout"), "40000"); - assertEquals(msg, hp.getProperty("foo", "film-service", "timeout"), "40000"); - assertEquals(msg, hp.getProperty("foo", "film-service", "port-of-cannes", "timeout"), "50000"); - } - - public void testGetPropertyUsingURI() { - String msg = "Returned value does not match expected value for this property!"; - assertEquals(msg, hp.getProperty("max-redirects"), "30"); - assertEquals(msg, hp.getProperty("http://bar.com", "brel-service", "max-redirects"), "40"); - assertEquals(msg, hp.getProperty("http://bar.com", "brel-service", "port-of-amsterdam", "max-redirects"), "60"); - assertEquals(msg, hp.getProperty("http://bar.com", "brel-service", "port-of-amsterdam", "timeout"), "40000"); - assertEquals(msg, hp.getProperty("http://foo.com", "film-service", "timeout"), "40000"); - assertEquals(msg, hp.getProperty("http://foo.com", "film-service", "port-of-cannes", "timeout"), "50000"); - - - assertEquals("Default value expected!", hp.getProperty("http://xyz", "unknown-service", "unknown-port", "timeout"), "40000"); - assertNull("Should return null when the property has no value", hp.getProperty("http://xyz", "unknown-service", "unknown-port", "unknown-property")); + assertEquals(msg, "30", hp.getProperty("max-redirects")); + assertEquals(msg, "40", hp.getProperty("http://bar.com", "brel-service", "max-redirects")); + assertEquals(msg, "60", hp.getProperty("http://bar.com", "brel-service", "port-of-amsterdam", "max-redirects")); + assertEquals(msg, "40000", hp.getProperty("http://bar.com", "brel-service", "port-of-amsterdam", "timeout")); + assertEquals(msg, "40000", hp.getProperty("http://foo.com", "film-service", "timeout")); + assertEquals(msg, "hi!", hp.getProperty("http://hello.com", "a_service", "worldproperty")); + assertEquals(msg, "4", hp.getProperty("a_namespace_with_no_alias", "a_service", "poolsize")); + assertEquals("If the same property is set by two different files, the order of precedence should be the alphabetical order.", "60000", hp.getProperty("http://foo.com", "film-service", "port-of-cannes", "timeout")); + assertEquals("The prefix could be use without interfering", "so green or red?", hp.getProperty("ode.a.property.beginning.with.the.prefix.but.no.service")); } public void testGetProperties() { final List keys = Arrays.asList("timeout", "max-redirects", "ode.a.property.beginning.with.the.prefix.but.no.service"); - Map map = hp.getProperties("foo", "film-service"); + Map map = hp.getProperties("http://foo.com", "film-service"); assertEquals("Number of properties is wrong", keys.size(), map.size()); assertEquals("40000", map.get("timeout")); assertEquals("30", map.get("max-redirects")); @@ -73,9 +63,9 @@ public void testCachedGetProperties() { - assertSame("Snapshot maps should be cached! References must be the same.", hp.getProperties("foo", "film-service"), hp.getProperties("foo", "film-service")); - assertSame("Snapshot maps should be cached! References must be the same.", hp.getProperties("foo", "film-service", "port-of-cannes"), hp.getProperties("foo", "film-service", "port-of-cannes")); - assertSame("Snapshot maps should be cached! References must be the same.", hp.getProperties("bla", "unknown-service"), hp.getProperties("bla", "unknown-service")); + assertSame("Snapshot maps should be cached!", hp.getProperties("foo", "film-service"), hp.getProperties("foo", "film-service")); + assertSame("Snapshot maps should be cached!", hp.getProperties("foo", "film-service", "port-of-cannes"), hp.getProperties("foo", "film-service", "port-of-cannes")); + assertSame("Snapshot maps should be cached!", hp.getProperties("bla", "unknown-service"), hp.getProperties("bla", "unknown-service")); } public void testWithNoFile() throws IOException { Copied: ode/trunk/utils/src/test/resources/hierarchical-1.properties (from r700885, ode/trunk/utils/src/test/resources/hierarchical.properties) URL: http://svn.apache.org/viewvc/ode/trunk/utils/src/test/resources/hierarchical-1.properties?p2=ode/trunk/utils/src/test/resources/hierarchical-1.properties&p1=ode/trunk/utils/src/test/resources/hierarchical.properties&r1=700885&r2=700972&rev=700972&view=diff ============================================================================== --- ode/trunk/utils/src/test/resources/hierarchical.properties (original) +++ ode/trunk/utils/src/test/resources/hierarchical-1.properties Wed Oct 1 16:44:37 2008 @@ -10,3 +10,5 @@ bar.brel-service.port-of-amsterdam.ode.max-redirects=60 ode.a.property.beginning.with.the.prefix.but.no.service=so green or red? + +a_namespace_with_no_alias.a_service.ode.poolsize=4 Added: ode/trunk/utils/src/test/resources/hierarchical-2.properties URL: http://svn.apache.org/viewvc/ode/trunk/utils/src/test/resources/hierarchical-2.properties?rev=700972&view=auto ============================================================================== --- ode/trunk/utils/src/test/resources/hierarchical-2.properties (added) +++ ode/trunk/utils/src/test/resources/hierarchical-2.properties Wed Oct 1 16:44:37 2008 @@ -0,0 +1,8 @@ +# define a different alias for the same namespace +alias.foo2=http://foo.com +alias.hello=http://hello.com + + +foo2.film-service.port-of-cannes.ode.timeout=60000 + +hello.a_service.ode.worldproperty=hi!
