diff --git a/axis2/src/main/java/org/apache/ode/axis2/EndpointReferenceContextImpl.java b/axis2/src/main/java/org/apache/ode/axis2/EndpointReferenceContextImpl.java index 577ebdb..5eb3f04 100644 --- a/axis2/src/main/java/org/apache/ode/axis2/EndpointReferenceContextImpl.java +++ b/axis2/src/main/java/org/apache/ode/axis2/EndpointReferenceContextImpl.java @@ -24,11 +24,15 @@ import javax.xml.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.bpel.epr.EndpointFactory; +import org.apache.ode.bpel.epr.MutableEndpoint; import org.apache.ode.bpel.iapi.EndpointReference; import org.apache.ode.bpel.iapi.EndpointReferenceContext; import org.apache.ode.utils.DOMUtils; import org.w3c.dom.Element; +import java.util.Map; +import java.util.HashMap; + public class EndpointReferenceContextImpl implements EndpointReferenceContext { private static final Log __log = LogFactory.getLog(EndpointReferenceContextImpl.class); @@ -46,4 +50,8 @@ public class EndpointReferenceContextImpl implements EndpointReferenceContext { EndpointReference endpoint = EndpointFactory.convert(qName, element); return endpoint; } + + public Map getConfigLookup(EndpointReference epr) { + return ((MutableEndpoint)epr).toMap(); + } } diff --git a/axis2/src/main/java/org/apache/ode/axis2/ODEServer.java b/axis2/src/main/java/org/apache/ode/axis2/ODEServer.java index c53fee8..7dc65cc 100644 --- a/axis2/src/main/java/org/apache/ode/axis2/ODEServer.java +++ b/axis2/src/main/java/org/apache/ode/axis2/ODEServer.java @@ -43,6 +43,7 @@ import org.apache.ode.bpel.iapi.ProcessConf; import org.apache.ode.bpel.iapi.ProcessStoreEvent; import org.apache.ode.bpel.iapi.ProcessStoreListener; import org.apache.ode.bpel.iapi.Scheduler; +import org.apache.ode.bpel.iapi.EndpointReferenceContext; import org.apache.ode.bpel.intercept.MessageExchangeInterceptor; import org.apache.ode.bpel.memdao.BpelDAOConnectionFactoryImpl; import org.apache.ode.bpel.pmapi.InstanceManagement; @@ -113,6 +114,7 @@ public class ODEServer { private ManagementService _mgtService; + public void init(ServletConfig config, AxisConfiguration axisConf) throws ServletException { init(config.getServletContext().getRealPath("/WEB-INF"), axisConf); } @@ -152,10 +154,11 @@ public class ODEServer { initDataSource(); __log.debug("Starting DAO."); initDAO(); + EndpointReferenceContextImpl eprContext = new EndpointReferenceContextImpl(this); __log.debug("Initializing BPEL process store."); - initProcessStore(); + initProcessStore(eprContext); __log.debug("Initializing BPEL server."); - initBpelServer(); + initBpelServer(eprContext); // Register BPEL event listeners configured in axis2.properties file. registerEventListeners(); @@ -441,14 +444,14 @@ public class ODEServer { } } - protected void initProcessStore() { - _store = createProcessStore(_db.getDataSource()); + protected void initProcessStore(EndpointReferenceContext eprContext) { + _store = createProcessStore(eprContext, _db.getDataSource()); _store.registerListener(new ProcessStoreListenerImpl()); _store.setDeployDir(new File(_workRoot, "processes")); } - protected ProcessStoreImpl createProcessStore(DataSource ds) { - return new ProcessStoreImpl(ds, _odeConfig.getDAOConnectionFactory(), _odeConfig, false); + protected ProcessStoreImpl createProcessStore(EndpointReferenceContext eprContext, DataSource ds) { + return new ProcessStoreImpl(eprContext, ds, _odeConfig.getDAOConnectionFactory(), _odeConfig, false); } protected Scheduler createScheduler() { @@ -458,7 +461,7 @@ public class ODEServer { return scheduler; } - private void initBpelServer() { + private void initBpelServer(EndpointReferenceContextImpl eprContext) { if (__log.isDebugEnabled()) { __log.debug("ODE initializing"); } @@ -483,7 +486,7 @@ public class ODEServer { _server.setDaoConnectionFactory(_daoCF); _server.setInMemDaoConnectionFactory(new BpelDAOConnectionFactoryImpl(_scheduler, _odeConfig.getInMemMexTtl())); - _server.setEndpointReferenceContext(new EndpointReferenceContextImpl(this)); + _server.setEndpointReferenceContext(eprContext); _server.setMessageExchangeContext(new MessageExchangeContextImpl(this)); _server.setBindingContext(new BindingContextImpl(this, _store)); _server.setScheduler(_scheduler); diff --git a/axis2/src/main/java/org/apache/ode/axis2/ODEService.java b/axis2/src/main/java/org/apache/ode/axis2/ODEService.java index 60f0403..91dac12 100644 --- a/axis2/src/main/java/org/apache/ode/axis2/ODEService.java +++ b/axis2/src/main/java/org/apache/ode/axis2/ODEService.java @@ -218,7 +218,7 @@ public class ODEService { * do not store the value so it can be dynamically updated */ private long getTimeout() { - String timeout = (String) _pconf.getProperties(_serviceName.getLocalPart(), _portName).get(Properties.PROP_MEX_TIMEOUT); + String timeout = _pconf.getEndpointProperties(_serviceRef).get(Properties.PROP_MEX_TIMEOUT); if (timeout != null) { try { return Long.parseLong(timeout); diff --git a/axis2/src/main/java/org/apache/ode/axis2/SoapExternalService.java b/axis2/src/main/java/org/apache/ode/axis2/SoapExternalService.java index 402ce65..f0f7698 100644 --- a/axis2/src/main/java/org/apache/ode/axis2/SoapExternalService.java +++ b/axis2/src/main/java/org/apache/ode/axis2/SoapExternalService.java @@ -22,6 +22,7 @@ package org.apache.ode.axis2; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.Map; +import java.util.HashMap; import java.io.File; import java.io.InputStream; @@ -47,6 +48,7 @@ import org.apache.ode.axis2.util.SoapMessageConverter; import org.apache.ode.bpel.epr.EndpointFactory; import org.apache.ode.bpel.epr.MutableEndpoint; import org.apache.ode.bpel.epr.WSAEndpoint; +import org.apache.ode.bpel.epr.WSDL11Endpoint; import org.apache.ode.bpel.iapi.BpelServer; import org.apache.ode.bpel.iapi.Message; import org.apache.ode.bpel.iapi.MessageExchange; @@ -87,6 +89,7 @@ public class SoapExternalService implements ExternalService { private Definition _definition; private QName _serviceName; private String _portName; + protected WSAEndpoint endpointReference; private AxisConfiguration _axisConfig; private SoapMessageConverter _converter; private Scheduler _sched; @@ -104,6 +107,12 @@ public class SoapExternalService implements ExternalService { _converter = new SoapMessageConverter(_definition, serviceName, portName); _server = server; _pconf = pconf; + + // initial endpoint reference + Element eprElmt = ODEService.genEPRfromWSDL(_definition, serviceName, portName); + if (eprElmt == null) + throw new IllegalArgumentException(msgs.msgPortDefinitionNotFound(serviceName, portName)); + endpointReference = EndpointFactory.convertToWSA(ODEService.createServiceRef(eprElmt)); } public void invoke(final PartnerRoleMessageExchange odeMex) { @@ -278,10 +287,7 @@ public class SoapExternalService implements ExternalService { } public org.apache.ode.bpel.iapi.EndpointReference getInitialEndpointReference() { - Element eprElmt = ODEService.genEPRfromWSDL(_definition, _serviceName, _portName); - if (eprElmt == null) - throw new IllegalArgumentException(msgs.msgPortDefinitionNotFound(_serviceName, _portName)); - return EndpointFactory.convertToWSA(ODEService.createServiceRef(eprElmt)); +return endpointReference; } public void close() { @@ -437,7 +443,7 @@ public class SoapExternalService implements ExternalService { } public Map lastModified() { - return _pconf.getProperties(_serviceName.getLocalPart(), _portName); + return _pconf.getEndpointProperties(endpointReference); } }); } @@ -452,7 +458,9 @@ public class SoapExternalService implements ExternalService { protected void doOnUpdate() { init(); - Map properties = _pconf.getProperties(_serviceName.getLocalPart(), _portName); + + // note: don't make this map an instance attribute, so we always get the latest version + final Map properties = _pconf.getEndpointProperties(endpointReference); Properties.Axis2.translate(properties, options); // set defaults values diff --git a/axis2/src/main/java/org/apache/ode/axis2/httpbinding/HttpExternalService.java b/axis2/src/main/java/org/apache/ode/axis2/httpbinding/HttpExternalService.java index 34584a1..be41a20 100644 --- a/axis2/src/main/java/org/apache/ode/axis2/httpbinding/HttpExternalService.java +++ b/axis2/src/main/java/org/apache/ode/axis2/httpbinding/HttpExternalService.java @@ -24,7 +24,6 @@ import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.URIException; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.params.HttpParams; -import org.apache.commons.httpclient.params.HttpClientParams; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.axis2.ExternalService; @@ -32,6 +31,8 @@ import org.apache.ode.axis2.ODEService; import org.apache.ode.axis2.Properties; import org.apache.ode.bpel.epr.EndpointFactory; import org.apache.ode.bpel.epr.WSAEndpoint; +import org.apache.ode.bpel.epr.WSDL11Endpoint; +import org.apache.ode.bpel.epr.MutableEndpoint; import org.apache.ode.bpel.iapi.BpelServer; import org.apache.ode.bpel.iapi.EndpointReference; import org.apache.ode.bpel.iapi.MessageExchange; @@ -57,6 +58,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.Map; +import java.util.HashMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; @@ -77,9 +79,9 @@ public class HttpExternalService implements ExternalService { protected ProcessConf pconf; protected QName serviceName; protected String portName; - - protected HttpClientHelper clientHelper; protected WSAEndpoint endpointReference; + + protected HttpClientHelper clientHelper; public HttpExternalService(ProcessConf pconf, QName serviceName, String portName, ExecutorService executorService, Scheduler scheduler, BpelServer server) { this.portName = portName; @@ -135,8 +137,8 @@ public class HttpExternalService implements ExternalService { public void invoke(PartnerRoleMessageExchange odeMex) { if (log.isDebugEnabled()) log.debug("Preparing " + getClass().getSimpleName() + " invocation..."); try { - // don't make this map a class attribute, so we always get the latest version - final Map properties = pconf.getProperties(serviceName.getLocalPart(), portName); + // note: don't make this map an instance attribute, so we always get the latest version + final Map properties = pconf.getEndpointProperties(endpointReference); final HttpParams params = Properties.HttpClient.translate(properties); // build the http method diff --git a/bpel-api/src/main/java/org/apache/ode/bpel/iapi/EndpointReferenceContext.java b/bpel-api/src/main/java/org/apache/ode/bpel/iapi/EndpointReferenceContext.java index f3513f6..cc37609 100644 --- a/bpel-api/src/main/java/org/apache/ode/bpel/iapi/EndpointReferenceContext.java +++ b/bpel-api/src/main/java/org/apache/ode/bpel/iapi/EndpointReferenceContext.java @@ -23,33 +23,42 @@ import javax.xml.namespace.QName; import org.w3c.dom.Element; +import java.util.Map; + /** - * Endpoint reference context: facililates the creation of + * Endpoint reference context: facililates the creation of * {@link EndpointReference} objects. - * */ public interface EndpointReferenceContext { - - /** - * Resolve an end-point reference from its XML representation. The - * nature of the representation is determined by the integration - * layer. The BPEL engine uses this method to reconstruct - * {@link EndpointReference} objects that have been persisted in the - * database via {@link EndpointReference#toXML(javax.xml.transform.Result)} - * method. - * - * @param XML representation of the EPR - * @return reconsistituted {@link EndpointReference} - */ - EndpointReference resolveEndpointReference(Element epr); - - - /** - * Converts an endpoint reference from its XML representation to another - * type of endpoint reference. - * @param targetType - * @param sourceEndpoint - * @return converted EndpointReference, being of targetType - */ - EndpointReference convertEndpoint(QName targetType, Element sourceEndpoint); + + /** + * Resolve an end-point reference from its XML representation. The + * nature of the representation is determined by the integration + * layer. The BPEL engine uses this method to reconstruct + * {@link EndpointReference} objects that have been persisted in the + * database via {@link EndpointReference#toXML(javax.xml.transform.Result)} + * method. + * + * @param XML representation of the EPR + * @return reconsistituted {@link EndpointReference} + */ + EndpointReference resolveEndpointReference(Element epr); + + + /** + * Converts an endpoint reference from its XML representation to another + * type of endpoint reference. + * + * @param targetType + * @param sourceEndpoint + * @return converted EndpointReference, being of targetType + */ + EndpointReference convertEndpoint(QName targetType, Element sourceEndpoint); + + /** + * + * @param epr + * @return + */ + Map getConfigLookup(EndpointReference epr); } diff --git a/bpel-api/src/main/java/org/apache/ode/bpel/iapi/ProcessConf.java b/bpel-api/src/main/java/org/apache/ode/bpel/iapi/ProcessConf.java index 1a18def..83f4105 100644 --- a/bpel-api/src/main/java/org/apache/ode/bpel/iapi/ProcessConf.java +++ b/bpel-api/src/main/java/org/apache/ode/bpel/iapi/ProcessConf.java @@ -108,10 +108,10 @@ public interface ProcessConf { List getFiles(); /** - * Get the deployment properties. + * Get the process properties. * @return */ - Map getDeploymentProperties(); + Map getProcessProperties(); /** * Gets the name of the package into which the process is deployed. @@ -157,10 +157,6 @@ public interface ProcessConf { boolean isEventEnabled(List scopeNames, BpelEvent.TYPE type); - /** - * Must not return null values. - * @param path - * @return - */ - public Map getProperties(String... path); + public Map getEndpointProperties(EndpointReference epr); + } diff --git a/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/ProcessAndInstanceManagementImpl.java b/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/ProcessAndInstanceManagementImpl.java index de07eae..ebea7a5 100644 --- a/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/ProcessAndInstanceManagementImpl.java +++ b/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/ProcessAndInstanceManagementImpl.java @@ -740,7 +740,7 @@ public class ProcessAndInstanceManagementImpl implements InstanceManagement, Pro TProcessProperties properties = info.addNewProperties(); if (custom.includeProcessProperties()) { - for (Map.Entry propEntry : pconf.getDeploymentProperties().entrySet()) { + for (Map.Entry propEntry : pconf.getProcessProperties().entrySet()) { TProcessProperties.Property tprocProp = properties.addNewProperty(); tprocProp.setName(new QName(propEntry.getKey().getNamespaceURI(), propEntry.getKey().getLocalPart())); Node propNode = tprocProp.getDomNode(); diff --git a/bpel-runtime/src/main/java/org/apache/ode/bpel/intercept/ThrottlingInterceptor.java b/bpel-runtime/src/main/java/org/apache/ode/bpel/intercept/ThrottlingInterceptor.java index b4288fe..e661f9d 100644 --- a/bpel-runtime/src/main/java/org/apache/ode/bpel/intercept/ThrottlingInterceptor.java +++ b/bpel-runtime/src/main/java/org/apache/ode/bpel/intercept/ThrottlingInterceptor.java @@ -57,7 +57,7 @@ public class ThrottlingInterceptor extends NoOpInterceptor { * @return value of the property, or null if not set */ private String getSimpleProperty(QName propertyName, InterceptorContext ic) { - Map props = ic.getProcessConf().getDeploymentProperties(); + Map props = ic.getProcessConf().getProcessProperties(); for (Map.Entry prop : props.entrySet()) { if (prop.getKey().equals(propertyName)) return ((Text)prop.getValue()).getWholeText(); diff --git a/bpel-runtime/src/test/java/org/apache/ode/bpel/runtime/MockBpelServer.java b/bpel-runtime/src/test/java/org/apache/ode/bpel/runtime/MockBpelServer.java index fd601cd..848d214 100644 --- a/bpel-runtime/src/test/java/org/apache/ode/bpel/runtime/MockBpelServer.java +++ b/bpel-runtime/src/test/java/org/apache/ode/bpel/runtime/MockBpelServer.java @@ -55,6 +55,7 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Properties; +import java.util.Collections; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -89,7 +90,7 @@ class MockBpelServer { _server.setInMemDaoConnectionFactory(new BpelDAOConnectionFactoryImpl(_scheduler)); if (_scheduler == null) throw new RuntimeException("No scheduler"); - _store = new ProcessStoreImpl(_dataSource,"jpa", new OdeConfigProperties(new Properties(), ""), true); + _store = new ProcessStoreImpl(_eprContext, _dataSource,"jpa", new OdeConfigProperties(new Properties(), ""), true); _server.setScheduler(_scheduler); _server.setEndpointReferenceContext(createEndpointReferenceContext()); _server.setMessageExchangeContext(createMessageExchangeContext()); @@ -209,6 +210,10 @@ class MockBpelServer { return (EndpointReference)_endpoints.get(service); } public EndpointReference convertEndpoint(QName qName, Element element) { return null; } + + public Map getConfigLookup(EndpointReference epr) { + return Collections.EMPTY_MAP; + } }; return _eprContext; } diff --git a/bpel-store/src/main/java/org/apache/ode/store/DeploymentUnitDir.java b/bpel-store/src/main/java/org/apache/ode/store/DeploymentUnitDir.java index 57e2cf2..3e009a2 100644 --- a/bpel-store/src/main/java/org/apache/ode/store/DeploymentUnitDir.java +++ b/bpel-store/src/main/java/org/apache/ode/store/DeploymentUnitDir.java @@ -59,7 +59,6 @@ import java.util.Set; */ class DeploymentUnitDir { - static final String IL_PROPERTIES_FILE = "integration-layer.properties"; private static Log __log = LogFactory.getLog(DeploymentUnitDir.class); @@ -202,8 +201,8 @@ class DeploymentUnitDir { return _duDirectory; } - public File getILPropertyFile(){ - return new File(getDeployDir(), IL_PROPERTIES_FILE); + public File getEPRConfigFile(){ + return new File(getDeployDir(), "endpoint-configuration.properties"); } public DeployDocument getDeploymentDescriptor() { diff --git a/bpel-store/src/main/java/org/apache/ode/store/ProcessConfImpl.java b/bpel-store/src/main/java/org/apache/ode/store/ProcessConfImpl.java index 124b561..b2cd4c4 100644 --- a/bpel-store/src/main/java/org/apache/ode/store/ProcessConfImpl.java +++ b/bpel-store/src/main/java/org/apache/ode/store/ProcessConfImpl.java @@ -52,9 +52,11 @@ import org.apache.ode.bpel.iapi.ContextException; import org.apache.ode.bpel.iapi.Endpoint; import org.apache.ode.bpel.iapi.ProcessConf; import org.apache.ode.bpel.iapi.ProcessState; +import org.apache.ode.bpel.iapi.EndpointReferenceContext; +import org.apache.ode.bpel.iapi.EndpointReference; import org.apache.ode.store.DeploymentUnitDir.CBPInfo; import org.apache.ode.utils.DOMUtils; -import org.apache.ode.utils.HierarchiedProperties; +import org.apache.ode.utils.HierarchicalProperties; import org.apache.ode.utils.fs.FileWatchDog; import org.apache.ode.utils.msg.MessageBundle; import org.w3c.dom.Element; @@ -89,14 +91,15 @@ public class ProcessConfImpl implements ProcessConf { private volatile boolean _inMemory = false; // provide the IL properties - private HierarchiedProperties _ilProperties; + private HierarchicalProperties ilProperties; // monitor the IL property file and reload it if necessary - private ILWatchDog _ilWatchDog; + private ILWatchDog ilWatchDog; private final ReadWriteLock ilPropertiesLock = new ReentrantReadWriteLock(); + private EndpointReferenceContext eprContext; ProcessConfImpl(QName pid, QName type, long version, DeploymentUnitDir du, TDeployment.Process pinfo, Date deployDate, - Map props, ProcessState pstate) { + Map props, ProcessState pstate, EndpointReferenceContext eprContext) { _pid = pid; _version = version; _du = du; @@ -106,8 +109,9 @@ public class ProcessConfImpl implements ProcessConf { _state = pstate; _type = type; _inMemory = _pinfo.isSetInMemory() && _pinfo.getInMemory(); - _ilWatchDog = new ILWatchDog(); - + this.eprContext = eprContext; + ilWatchDog = new ILWatchDog(); + initLinks(); initMexInterceptors(); initEventList(); @@ -176,7 +180,7 @@ public class ProcessConfImpl implements ProcessConf { return _du.getName(); } - public Map getDeploymentProperties() { + public Map getProcessProperties() { return _props; } @@ -370,27 +374,21 @@ public class ProcessConfImpl implements ProcessConf { } } - /** - * - * @param path only the 2 first elements would be interpreted as the service local name and port name. all others would be ignored. - * @return a map of properties. - */ - public Map getProperties(String... path) { - String service=null, port=null; - if(path.length>=1) service = path[0]; - if(path.length>=2) port = path[1]; - if(path.length>2) if(__log.isWarnEnabled()) __log.debug("Arguments with index>2 ignored!"); + public Map getEndpointProperties(EndpointReference epr) { + final Map map = eprContext.getConfigLookup(epr); + final QName service = (QName) map.get("service"); + final String port = (String) map.get("port"); // update properties if necessary // do it manually to save resources (instead of using a thread) - _ilWatchDog.check(); - if (_ilProperties == null) { + ilWatchDog.check(); + if (ilProperties == null) { return Collections.EMPTY_MAP; } else { // take a lock so we can have a consistent snapshot of the properties ilPropertiesLock.readLock().lock(); try { - return _ilProperties.getProperties(service, port); + return ilProperties.getProperties(service, port); } finally { ilPropertiesLock.readLock().unlock(); } @@ -403,20 +401,20 @@ public class ProcessConfImpl implements ProcessConf { */ private class ILWatchDog extends FileWatchDog { public ILWatchDog() { - super(_du.getILPropertyFile()); + super(_du.getEPRConfigFile()); } protected void init() { ilPropertiesLock.writeLock().lock(); try { - if (_ilProperties == null) { + if (ilProperties == null) { try { - _ilProperties = new HierarchiedProperties(super.file); + ilProperties = new HierarchicalProperties(super.file); } catch (IOException e) { throw new ContextException("Integration-Layer Properties cannot be loaded!", e); } } else { - _ilProperties.clear(); + ilProperties.clear(); } } finally { ilPropertiesLock.writeLock().unlock(); @@ -424,7 +422,7 @@ public class ProcessConfImpl implements ProcessConf { } protected boolean isInitialized() { - return _ilProperties != null; + return ilProperties != null; } protected void doOnUpdate() { @@ -432,7 +430,7 @@ public class ProcessConfImpl implements ProcessConf { try { init(); try { - _ilProperties.loadFile(); + ilProperties.loadFile(); } catch (IOException e) { throw new ContextException("Integration-Layer Properties cannot be loaded!", e); } diff --git a/bpel-store/src/main/java/org/apache/ode/store/ProcessStoreImpl.java b/bpel-store/src/main/java/org/apache/ode/store/ProcessStoreImpl.java index a8b2c6e..9a79832 100644 --- a/bpel-store/src/main/java/org/apache/ode/store/ProcessStoreImpl.java +++ b/bpel-store/src/main/java/org/apache/ode/store/ProcessStoreImpl.java @@ -29,6 +29,7 @@ import org.apache.ode.bpel.iapi.ProcessState; import org.apache.ode.bpel.iapi.ProcessStore; import org.apache.ode.bpel.iapi.ProcessStoreEvent; import org.apache.ode.bpel.iapi.ProcessStoreListener; +import org.apache.ode.bpel.iapi.EndpointReferenceContext; import org.apache.ode.store.DeploymentUnitDir.CBPInfo; import org.apache.ode.utils.DOMUtils; import org.apache.ode.utils.GUID; @@ -90,6 +91,8 @@ public class ProcessStoreImpl implements ProcessStore { private ConfStoreConnectionFactory _cf; + private EndpointReferenceContext eprContext; + protected File _deployDir; /** @@ -106,10 +109,11 @@ public class ProcessStoreImpl implements ProcessStore { private DataSource _inMemDs; public ProcessStoreImpl() { - this(null, "", new OdeConfigProperties(new Properties(), ""), true); + this(null, null, "", new OdeConfigProperties(new Properties(), ""), true); } - public ProcessStoreImpl(DataSource ds, String persistenceType, OdeConfigProperties props, boolean auto) { + public ProcessStoreImpl(EndpointReferenceContext eprContext, DataSource ds, String persistenceType, OdeConfigProperties props, boolean auto) { + this.eprContext = eprContext; if (ds != null) { // ugly hack if (persistenceType.toLowerCase().indexOf("hib") != -1) @@ -212,7 +216,7 @@ public class ProcessStoreImpl implements ProcessStore { // final OProcess oprocess = loadCBP(cbpInfo.cbp); ProcessConfImpl pconf = new ProcessConfImpl(pid, processDD.getName(), version, du, processDD, deployDate, - calcInitialProperties(processDD), calcInitialState(processDD)); + calcInitialProperties(processDD), calcInitialState(processDD), eprContext); processes.add(pconf); } @@ -256,7 +260,7 @@ public class ProcessStoreImpl implements ProcessStore { try { ProcessConfDAO newDao = dudao.createProcess(pc.getProcessId(), pc.getType(), pc.getVersion()); newDao.setState(pc.getState()); - for (Map.Entry prop : pc.getDeploymentProperties().entrySet()) { + for (Map.Entry prop : pc.getProcessProperties().entrySet()) { newDao.setProperty(prop.getKey(), DOMUtils.domToString(prop.getValue())); } deployed.add(pc.getProcessId()); @@ -621,7 +625,7 @@ public class ProcessStoreImpl implements ProcessStore { // 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()); + .getDeployDate(), props, p.getState(), eprContext); version = p.getVersion(); _processes.put(pconf.getProcessId(), pconf); diff --git a/bpel-test/src/main/java/org/apache/ode/test/BPELTestAbstract.java b/bpel-test/src/main/java/org/apache/ode/test/BPELTestAbstract.java index 5c3c18d..7fe5bd9 100644 --- a/bpel-test/src/main/java/org/apache/ode/test/BPELTestAbstract.java +++ b/bpel-test/src/main/java/org/apache/ode/test/BPELTestAbstract.java @@ -134,7 +134,7 @@ public abstract class BPELTestAbstract { _server.setBindingContext(new BindingContextImpl()); _server.setMessageExchangeContext(mexContext); scheduler.setJobProcessor(_server); - store = new ProcessStoreImpl(null, "jpa", new OdeConfigProperties(new Properties(), ""), true); + store = new ProcessStoreImpl(null, null, "jpa", new OdeConfigProperties(new Properties(), ""), true); store.registerListener(new ProcessStoreListener() { public void onProcessStoreEvent(ProcessStoreEvent event) { // bounce the process diff --git a/jbi/src/main/java/org/apache/ode/jbi/EndpointReferenceContextImpl.java b/jbi/src/main/java/org/apache/ode/jbi/EndpointReferenceContextImpl.java index 142441b..89d7acb 100644 --- a/jbi/src/main/java/org/apache/ode/jbi/EndpointReferenceContextImpl.java +++ b/jbi/src/main/java/org/apache/ode/jbi/EndpointReferenceContextImpl.java @@ -26,17 +26,20 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.bpel.iapi.EndpointReference; import org.apache.ode.bpel.iapi.EndpointReferenceContext; +import org.apache.ode.bpel.epr.MutableEndpoint; import org.apache.ode.utils.DOMUtils; import org.w3c.dom.Document; import org.w3c.dom.DocumentFragment; import org.w3c.dom.Element; import org.w3c.dom.NodeList; +import java.util.Map; + /** * Implementation of the ODE {@link org.apache.ode.bpel.iapi.EndpointReferenceContext} - * interface used by the BPEL engine to convert XML descriptions of endpoint + * interface used by the BPEL engine to convert XML descriptions of endpoint * references (EPRs) into Java object representations. In the JBI context all - * endpoint references are considered to be JBI + * endpoint references are considered to be JBI * {@link javax.jbi.servicedesc.ServiceEndpoint}s are resolved by using the * {@link javax.jbi.component.ComponentContext#resolveEndpointReference(org.w3c.dom.DocumentFragment)} * method. Note that is is possible to resolve both "internal" and "external" @@ -50,21 +53,21 @@ public class EndpointReferenceContextImpl implements EndpointReferenceContext { private final OdeContext _ode; static final QName JBI_EPR = new QName("http://java.sun.com/jbi/end-point-reference", "end-point-reference"); - + public EndpointReferenceContextImpl(OdeContext ode) { _ode = ode; } public EndpointReference resolveEndpointReference(Element epr) { QName elname = new QName(epr.getNamespaceURI(),epr.getLocalName()); - + if (__log.isDebugEnabled()) { __log.debug( "resolveEndpointReference:\n" + prettyPrint( epr ) ); } if (!elname.equals(EndpointReference.SERVICE_REF_QNAME)) throw new IllegalArgumentException("EPR root element " + elname + " should be " + EndpointReference.SERVICE_REF_QNAME); - + Document doc = DOMUtils.newDocument(); DocumentFragment fragment = doc.createDocumentFragment(); NodeList children = epr.getChildNodes(); @@ -75,7 +78,7 @@ public class EndpointReferenceContextImpl implements EndpointReferenceContext { return null; return new JbiEndpointReference(se); } - + public EndpointReference convertEndpoint(QName eprType, Element epr) { Document doc = DOMUtils.newDocument(); @@ -86,11 +89,11 @@ public class EndpointReferenceContextImpl implements EndpointReferenceContext { ServiceEndpoint se = _ode.getContext().resolveEndpointReference(fragment); if (se == null) return null; - + return new JbiEndpointReference(se, eprType); - + } - + public static QName convertClarkQName(String name) { int pos = name.indexOf('}'); if ( name.startsWith("{") && pos > 0 ) { @@ -100,7 +103,7 @@ public class EndpointReferenceContextImpl implements EndpointReferenceContext { } return new QName( name ); } - + private String prettyPrint( Element el ) { try { return DOMUtils.prettyPrint( el ); @@ -108,4 +111,8 @@ public class EndpointReferenceContextImpl implements EndpointReferenceContext { return ioe.getMessage(); } } + + public Map getConfigLookup(EndpointReference epr) { + return ((MutableEndpoint)epr).toMap(); + } } diff --git a/jbi/src/main/java/org/apache/ode/jbi/OdeLifeCycle.java b/jbi/src/main/java/org/apache/ode/jbi/OdeLifeCycle.java index 5ebe511..3299cee 100755 --- a/jbi/src/main/java/org/apache/ode/jbi/OdeLifeCycle.java +++ b/jbi/src/main/java/org/apache/ode/jbi/OdeLifeCycle.java @@ -221,7 +221,7 @@ public class OdeLifeCycle implements ComponentLifeCycle { _ode._scheduler.setExecutorService(_ode._executorService); _ode._scheduler.setTransactionManager((TransactionManager) _ode.getContext().getTransactionManager()); - _ode._store = new ProcessStoreImpl(_ode._dataSource, _ode._config.getDAOConnectionFactory(), _ode._config, false); + _ode._store = new ProcessStoreImpl(_ode._eprContext , _ode._dataSource, _ode._config.getDAOConnectionFactory(), _ode._config, false); _ode._store.loadAll(); _ode._server.setInMemDaoConnectionFactory(new org.apache.ode.bpel.memdao.BpelDAOConnectionFactoryImpl( diff --git a/utils/src/main/java/org/apache/ode/utils/HierarchicalProperties.java b/utils/src/main/java/org/apache/ode/utils/HierarchicalProperties.java new file mode 100644 index 0000000..6059bc2 --- /dev/null +++ b/utils/src/main/java/org/apache/ode/utils/HierarchicalProperties.java @@ -0,0 +1,470 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ode.utils; + +import org.apache.commons.collections.map.MultiKeyMap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.xml.namespace.QName; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.Collections; +import java.util.Iterator; + +/** + * This class load a regular property file in {@link java.util.Properties} instance. 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. + *

+ * A prefix must be defined to discriminate the property name and the level-1, level-2 names. The default prefix is {@link #ODE_PREFFIX}. + *

+ * Properties must respect the following pattern: [level1.[level2.]prefix.]property + *

+ * A concrete use case could be the definition of properties for wsdl services and ports. + *
Level 0 would be: values common to all services and ports. + *
Level 1: values common to a given service. + *
Level 2: values common to a given port. + *

+ * For instance, if the property file looks like this: + *

+ * timeout=40000
+ * film-service.port-of-cannes.ode.timeout=50000
+ * 

+ * max-redirects=30 + * brel-service.ode.max-redirects=40 + * brel-service.port-of-amsterdam.ode.max-redirects=60 + *

+ * The following values may be expected: + *
+ * getProperty("max-redirects")                                       => 30
+ * getProperty("brel-service", "max-redirects")                       => 40
+ * getProperty("brel-service", "port-of-amsterdam", "max-redirects")  => 60
+ * 

+ * getProperty("film-service", "timeout") => 40000 + * getProperty("film-service", "port-of-cannes", "timeout") => 50000 + * getProperty("brel-service", "port-of-amsterdam", "timeout") => 40000 + *

+ *

+ * Values may contain some environment variables. For instance, message=You're using ${java.version}. + *

+ * This class is not thread-safe. + * + * @author Alexis Midon + */ +public class HierarchicalProperties { + + private static final Log log = LogFactory.getLog(HierarchicalProperties.class); + + public static final String ODE_PREFFIX = "ode"; + + // the raw properties as of loaded from the filesystem + private Properties props = new Properties(); + private Map aliases = new HashMap(); + private File file; + private String prefix; + private String dotted_prefix; + /* + This map contains ChainedMap instances chained according to the 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. + + The ChainedMap instance common to all services and ports is associated to the [null, null] key. + ChainedMap instance common to all ports of a given service is associated to [service, null]. + ChainedMap instance of a given service, port couple is associated to [service, port]. + + The ChainedMap instances contain string values as loaded from the filesystem. + */ + private MultiKeyMap hierarchicalMap = new MultiKeyMap(); + + // map used to cache immutable versions of the maps + private transient MultiKeyMap cacheOfImmutableMaps = new MultiKeyMap(); + + /** + * @param file 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 + */ + public HierarchicalProperties(File file, String prefix) throws IOException { + this.file = file; + this.prefix = prefix; + this.dotted_prefix = "." + prefix + "."; + loadFile(); + } + + public HierarchicalProperties(File file) throws IOException { + this(file, ODE_PREFFIX); + } + + /** + * Clear all existing content, 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 { + if (!file.exists()) { + if (log.isDebugEnabled()) log.debug("File does not exist [" + file + "] Properties will be empty."); + return; + } + // #1. clear all existing content + clear(); + + // #2. read the file + FileInputStream fis = new FileInputStream(file); + try { + if (log.isDebugEnabled()) log.debug("Loading property file: " + file); + props.load(fis); + } finally { + fis.close(); + } + + // #3. put the root map + hierarchicalMap.put(null, null, new ChainedMap()); + + // #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(); + + // replace any env variables by its value + value = SystemUtils.replaceSystemProperties(value); + props.put(key, value); + + 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); + } + + // create the map itself and link it to theservice map + p = new ChainedMap(s); + // put it in the multi-map + hierarchicalMap.put(qname, port, p); + } + + // save the key/value in its chained map + p.put(targetedProperty, value); + } + } + } + + /** + * Clear all content. If {@link #loadFile()} is not invoked later, all returned values will be null. + */ + public void clear() { + props.clear(); + aliases.clear(); + hierarchicalMap.clear(); + cacheOfImmutableMaps.clear(); + } + + protected ChainedMap getRootMap() { + Object o = hierarchicalMap.get(null, null); + if (o == null) { + o = new ChainedMap(); + hierarchicalMap.put(null, null, o); + } + return (ChainedMap) o; + } + + public Map getProperties(String serviceNamespaceURI, String serviceLocalPart) { + return getProperties(new QName(serviceNamespaceURI, serviceLocalPart)); + } + + /** + * @param service + * @return a map containing all the properties for the given service. + * @see #getProperties(String, String) + */ + public Map getProperties(QName service) { + return getProperties(service, null); + } + + 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 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 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 = resolveAlias(service); + // else check the cache of ChainedMap already converted into immutable maps + Map cachedMap = (Map) this.cacheOfImmutableMaps.get(service, port); + if (cachedMap != null) { + return cachedMap; + } + + // 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) { + // return the cached version of the root map + return getProperties((QName) null, null); + } + } + 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 at each invocation + this.cacheOfImmutableMaps.put(service, port, snapshotMap); + return snapshotMap; + } + + public String getProperty(String property) { + return (String) getRootMap().get(property); + } + + public String getProperty(String serviceNamespaceURI, String serviceLocalPart, String property) { + return getProperty(new QName(serviceNamespaceURI, serviceLocalPart), property); + } + + public String getProperty(QName service, String property) { + return getProperty(service, null, property); + } + + public String getProperty(String serviceNamespaceURI, String serviceLocalPart, String port, String property) { + return getProperty(new QName(serviceNamespaceURI, serviceLocalPart), port, property); + } + + public String getProperty(QName service, String port, String property) { + ChainedMap cm = (ChainedMap) hierarchicalMap.get(resolveAlias(service), port); + // if this port is not explicitly mentioned in the multimap, get the default values. + if (cm == null) cm = getRootMap(); + return (String) cm.get(property); + } + + public String getPrefix() { + return prefix; + } + + private QName resolveAlias(QName service) { + if (service != null && aliases.containsKey(service.getNamespaceURI())) { + return new QName(aliases.get(service.getNamespaceURI()), service.getLocalPart()); + } + return service; + } + + private String[] parseProperty(String property) { + // aliaas ns, service, port, targeted property + String[] res = new String[4]; + + int index = property.indexOf(dotted_prefix); + if (index <= 0) { + // assume there is no service/port prefixed, no need to go further + res[3] = property; + } else { + res[3] = property.substring(index + dotted_prefix.length()); // targeted property + String prefix = property.substring(0, index); + String[] t = prefix.split("\\."); + if (t.length != 2 && t.length != 3) { + throw new IllegalArgumentException("Invalid property name:" + property + " Expected pattern: [nsalias.service.[port.]" + prefix + ".]property"); + } + if (t.length >= 2) { + res[0] = t[0]; // ns alias + res[1] = t[1]; // service name + } + if (t.length > 2) { + res[2] = t[2]; // port name + } + } + return res; + } + + /** + * Link two Maps instances in a parent-child relation. Meaning that if a key is looked up but not found on the child, + * then the key will be looked up on the parent map. + *
The raison d'etre of this class is to the {@link #keySet()} method. This methods returns a set of all the keys contained in the child and the parent. + * That's the main reason to not used the {@link java.util.Properties} class (which offers access to child keys only). + *

The child has an immutable view of the parent map. Methods {@link #clear()} and {@link #remove(Object)} + * throw {@link UnsupportedOperationException}. Methods {@link #put(Object, Object)} and {@link #putAll(java.util.Map)} impacts only the child map. + *
Methods {@link #clearLocally(Object)} + *

+ * This class does NOT implement the {@link java.util.Map} interface because methods {@link java.util.Map#entrySet()} }, + * {@link java.util.Map#values()} and {@link java.util.Map#keySet()} would NOT be backed by the Map itself. + *
Contributions welcome to implement that part. + * + * @author Alexis Midon + */ + private static class ChainedMap { + + private ChainedMap parent; + private Map child; + + public ChainedMap() { + parent = null; + child = new HashMap(); + } + + public ChainedMap(ChainedMap parent) { + this.parent = parent; + this.child = new HashMap(); + } + + public ChainedMap getParent() { + return parent; + } + + public void setParent(ChainedMap parent) { + this.parent = parent; + } + + /** + * Perfom a look up on the child map only. + */ + public Object getLocally(Object key) { + return child.get(key); + } + + /** + * Clear the child map only, the parent map is not altered. + */ + public void clearLocally() { + child.clear(); + } + + /** + * Perform a look up for the given key on the child map, and if not found then perform the look up on the parent map. + * + * @param key + * @return + */ + public Object get(Object key) { + Object lv = getLocally(key); + if (lv != null) return lv; + else if (parent != null) return parent.get(key); + return null; + } + + /** + * Put this pair in the child map. + */ + public Object put(Object key, Object value) { + if (key == null) throw new NullPointerException("Null keys forbidden!"); + return child.put(key, value); + } + + + /** + * Put these pairs in the child map. + */ + public void putAll(Map t) { + for (Object e : t.entrySet()) { + put(((Map.Entry) e).getKey(), ((Map.Entry) e).getValue()); + } + } + + /** + * @throws UnsupportedOperationException + * @see #clearLocally() + */ + public void clear() { + throw new UnsupportedOperationException(); + } + + /** + * @throws UnsupportedOperationException + */ + public Object remove(Object key) { + throw new UnsupportedOperationException(); + } + + /** + * @return true if the child map is empty AND the parent map is null or empty as well. + *

child.isEmpty() && (parent == null || parent.isEmpty());
+ */ + public boolean isEmpty() { + return child.isEmpty() && (parent == null || parent.isEmpty()); + } + + /** + * @return true if the child map contains this key OR the parent map is not null and contains this key. + *
child.containsKey(key) || (parent != null && parent.containsKey(key));
+ */ + public boolean containsKey(Object key) { + if (key == null) throw new NullPointerException("Null keys forbidden!"); + return child.containsKey(key) || (parent != null && parent.containsKey(key)); + } + + /** + * @return true if the child map contains this value OR the parent is not null + *
child.containsValue(value) || (parent != null && parent.containsValue(value));
+ */ + public boolean containsValue(Object value) { + return child.containsValue(value) || (parent != null && parent.containsValue(value)); + } + + public int size() { + return keySet().size(); + } + + /** + * @return a new set instance merging all keys contained in the child and parent maps. The returned set is not backed by the maps. + * Any references to the returned sets are hold at the holder's own risks. This breaks the general {@link java.util.Map#entrySet()} contract. + */ + public Set keySet() { + HashSet s = new HashSet(child.keySet()); + if (parent != null) s.addAll(parent.keySet()); + return s; + } + } +} diff --git a/utils/src/main/java/org/apache/ode/utils/HierarchiedProperties.java b/utils/src/main/java/org/apache/ode/utils/HierarchiedProperties.java deleted file mode 100644 index 56f4a6f..0000000 --- a/utils/src/main/java/org/apache/ode/utils/HierarchiedProperties.java +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.ode.utils; - -import org.apache.commons.collections.map.MultiKeyMap; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.Collections; - -/** - * This class load a regular property file in {@link java.util.Properties} instance. The main feature is that property can - * be hierarchied/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. - *

- * A prefix must be defined to discriminate the property name and the level-1, level-2 names. The default prefix is {@link #ODE_PREFFIX}. - *

- * Properties must respect the following pattern: [level1[.level2].prefix.]property - *

- * A concrete use case could be the definition of properties for wsdl services and ports. - *
Level 0 would be: values common to all services and ports. - *
Level 1: values common to a given service. - *
Level 2: values common to a given port. - *

- * For instance, if the property file looks like this: - *

- * timeout=40000
- * film-service.port-of-cannes.ode.timeout=50000
- * 

- * max-redirects=30 - * brel-service.ode.max-redirects=40 - * brel-service.port-of-amsterdam.ode.max-redirects=60 - *

- * The following values may be expected: - *
- * getProperty("max-redirects")                                       => 30
- * getProperty("brel-service", "max-redirects")                       => 40
- * getProperty("brel-service", "port-of-amsterdam", "max-redirects")  => 60
- * 

- * getProperty("film-service", "timeout") => 40000 - * getProperty("film-service", "port-of-cannes", "timeout") => 50000 - * getProperty("brel-service", "port-of-amsterdam", "timeout") => 40000 - *

- *

- * Values may contain some environment variables. For instance, message=You're using ${java.version}. - *

- * This class is not thread-safe. - * - * @author Alexis Midon - */ -public class HierarchiedProperties { - - private static final Log log = LogFactory.getLog(HierarchiedProperties.class); - - // Prefix used by all properties - public static final String ODE_PREFFIX = "ode."; - - // the raw properties as of loaded from the filesystem - private Properties props = new Properties(); - private File file; - private String prefix; - /* - This map contains ChainedMap instances hierarchied according to the 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. - - The ChainedMap instance common to all services and ports is associated to the [null, null] key. - ChainedMap instance common to all ports of a given service is associated to [service, null]. - ChainedMap instance of a given service, port couple is associated to [service, port]. - - The ChainedMap instances contain string values as loaded from the filesystem. - */ - private MultiKeyMap hierarchiedMap = new MultiKeyMap(); - - // map used to cache immutable versions of the maps - private transient MultiKeyMap cacheOfImmutableMaps = new MultiKeyMap(); - - /** - * @param file 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 - */ - public HierarchiedProperties(File file, String prefix) throws IOException { - this.file = file; - this.prefix = prefix; - loadFile(); - } - - public HierarchiedProperties(File file) throws IOException { - this(file, ODE_PREFFIX); - } - - /** - * Clear all existing content, 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 { - if (!file.exists()) { - if (log.isDebugEnabled()) log.debug("File does not exist [" + file + "] Properties will be empty."); - return; - } - // #1. clear all existing content - clear(); - - // #2. read the file - FileInputStream fis = new FileInputStream(file); - try { - if (log.isDebugEnabled()) log.debug("Loading property file: " + file); - props.load(fis); - } finally { - fis.close(); - } - - // #3. put the root map - hierarchiedMap.put(null, null, new ChainedMap()); - - // #4. process each property - for (Object key : props.keySet()) { - String value = (String) props.get(key); - - // replace any env variables by its value - value = SystemUtils.replaceSystemProperties(value); - props.put(key, value); - - // parse the property name - String[] info = parseProperty((String) key); - String service = info[0]; - String port = info[1]; - String targetedProperty = info[2]; - - // get the map associated to this port - ChainedMap p = (ChainedMap) hierarchiedMap.get(service, port); - if (p == null) { - // create it if necessary - // get the associated service map - ChainedMap s = (ChainedMap) hierarchiedMap.get(service, 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 - hierarchiedMap.put(service, null, s); - } - - // create the map itself and link it to theservice map - p = new ChainedMap(s); - // put it in the multi-map - hierarchiedMap.put(service, port, p); - } - - // save the key/value in its chained map - p.put(targetedProperty, value); - } - } - - /** - * Clear all content. If {@link #loadFile()} is not invoked later, all returned values will be null. - */ - public void clear() { - props.clear(); - hierarchiedMap.clear(); - cacheOfImmutableMaps.clear(); - } - - protected ChainedMap getRootMap() { - Object o = hierarchiedMap.get(null, null); - if (o == null) { - o = new ChainedMap(); - hierarchiedMap.put(null, null, o); - } - return (ChainedMap) o; - } - - /** - * @param service - * @return a map containing all the properties for the given service. - * @see #getProperties(String, String) - */ - public Map getProperties(String service) { - return getProperties(service, null); - } - - /** - * 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 a map containing all the properties for the given port - */ - public Map getProperties(String service, String port) { - // no need to go further if no properties - if (hierarchiedMap.isEmpty()) return Collections.EMPTY_MAP; - - // else check the cache of ChainedMap already converted into immutable maps - Map cachedMap = (Map) this.cacheOfImmutableMaps.get(service, port); - if (cachedMap != null) { - return cachedMap; - } - - // else get the corresponding ChainedMap and convert it into a Map - ChainedMap cm = (ChainedMap) hierarchiedMap.get(service, port); - // if this port is not explicitly mentioned in the multimap, get the default values. - if (cm == null) { - cm = (ChainedMap) hierarchiedMap.get(service, null); - if (cm == null) { - // return the cached version of the root map - return getProperties(null, null); - } - } - 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 at each invocation - this.cacheOfImmutableMaps.put(service, port, snapshotMap); - return snapshotMap; - } - - public String getProperty(String property) { - return (String) getRootMap().get(property); - } - - public String getProperty(String service, String property) { - return getProperty(service, null, property); - } - - public String getProperty(String service, String port, String property) { - ChainedMap cm = (ChainedMap) hierarchiedMap.get(service, port); - // if this port is not explicitly mentioned in the multimap, get the default values. - if (cm == null) cm = getRootMap(); - return (String) cm.get(property); - } - - public String getPrefix() { - return prefix; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - private String[] parseProperty(String property) { - // service, port, targeted property - String[] res = new String[3]; - - int index = property.indexOf(prefix); - if (index <= 0) { - // assume there is no service/port prefixed, no need to go further - res[2] = property; - } else { - res[2] = property.substring(index + prefix.length()); // targeted property - String prefix = property.substring(0, index); - String[] t = prefix.split("\\."); - if (t.length > 2) { - throw new IllegalArgumentException("'.' cannot be mentioned more than twice in the before the property prefix"); - } - if (t.length >= 1) { - res[0] = t[0]; // service name - } - if (t.length > 1) { - res[1] = t[1]; // port name - } - } - return res; - } - - /** - * Link two Maps instances in a parent-child relation. Meaning that if a key is looked up but not found on the child, - * then the key will be looked up on the parent map. - *
The raison d'etre of this class is to the {@link #keySet()} method. This methods returns a set of all the keys contained in the child and the parent. - * That's the main reason to not used the {@link java.util.Properties} class (which offers access to child keys only). - *

The child has an immutable view of the parent map. Methods {@link #clear()} and {@link #remove(Object)} - * throw {@link UnsupportedOperationException}. Methods {@link #put(Object, Object)} and {@link #putAll(java.util.Map)} impacts only the child map. - *
Methods {@link #clearLocally(Object)} - *

- * This class does NOT implement the {@link java.util.Map} interface because methods {@link java.util.Map#entrySet()} }, - * {@link java.util.Map#values()} and {@link java.util.Map#keySet()} would NOT be backed by the Map itself. - *
Contributions welcome to implement that part. - * - * @author Alexis Midon - */ - private static class ChainedMap { - - private ChainedMap parent; - private Map child; - - public ChainedMap() { - parent = null; - child = new HashMap(); - } - - public ChainedMap(ChainedMap parent) { - this.parent = parent; - this.child = new HashMap(); - } - - public ChainedMap getParent() { - return parent; - } - - public void setParent(ChainedMap parent) { - this.parent = parent; - } - - /** - * Perfom a look up on the child map only. - */ - public Object getLocally(Object key) { - return child.get(key); - } - - /** - * Clear the child map only, the parent map is not altered. - */ - public void clearLocally() { - child.clear(); - } - - /** - * Perform a look up for the given key on the child map, and if not found then perform the look up on the parent map. - * - * @param key - * @return - */ - public Object get(Object key) { - Object lv = getLocally(key); - if (lv != null) return lv; - else if (parent != null) return parent.get(key); - return null; - } - - /** - * Put this pair in the child map. - */ - public Object put(Object key, Object value) { - if (key == null) throw new NullPointerException("Null keys forbidden!"); - return child.put(key, value); - } - - - /** - * Put these pairs in the child map. - */ - public void putAll(Map t) { - for (Object e : t.entrySet()) { - put(((Map.Entry) e).getKey(), ((Map.Entry) e).getValue()); - } - } - - /** - * @throws UnsupportedOperationException - * @see #clearLocally() - */ - public void clear() { - throw new UnsupportedOperationException(); - } - - /** - * @throws UnsupportedOperationException - */ - public Object remove(Object key) { - throw new UnsupportedOperationException(); - } - - /** - * @return true if the child map is empty AND the parent map is null or empty as well. - *

child.isEmpty() && (parent == null || parent.isEmpty());
- */ - public boolean isEmpty() { - return child.isEmpty() && (parent == null || parent.isEmpty()); - } - - /** - * @return true if the child map contains this key OR the parent map is not null and contains this key. - *
child.containsKey(key) || (parent != null && parent.containsKey(key));
- */ - public boolean containsKey(Object key) { - if (key == null) throw new NullPointerException("Null keys forbidden!"); - return child.containsKey(key) || (parent != null && parent.containsKey(key)); - } - - /** - * @return true if the child map contains this value OR the parent is not null - *
child.containsValue(value) || (parent != null && parent.containsValue(value));
- */ - public boolean containsValue(Object value) { - return child.containsValue(value) || (parent != null && parent.containsValue(value)); - } - - public int size() { - return keySet().size(); - } - - /** - * @return a new set instance merging all keys contained in the child and parent maps. The returned set is not backed by the maps. - * Any references to the returned sets are hold at the holder's own risks. This breaks the general {@link java.util.Map#entrySet()} contract. - */ - public Set keySet() { - HashSet s = new HashSet(child.keySet()); - if (parent != null) s.addAll(parent.keySet()); - return s; - } - } -} diff --git a/utils/src/test/java/org/apache/ode/utils/HierarchicalPropertiesTest.java b/utils/src/test/java/org/apache/ode/utils/HierarchicalPropertiesTest.java new file mode 100644 index 0000000..f4ba1d1 --- /dev/null +++ b/utils/src/test/java/org/apache/ode/utils/HierarchicalPropertiesTest.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ode.utils; + +import junit.framework.TestCase; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.List; +import java.util.Arrays; + +/** + * @author Alexis Midon + */ +public class HierarchicalPropertiesTest extends TestCase { + protected HierarchicalProperties hp; + + protected void setUp() throws Exception { + File file = new File(getClass().getResource("/hierarchical.properties").toURI()); + hp = new HierarchicalProperties(file); + } + + public void testGetPropertyUsingAliases() { + 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"); + assertEquals(msg, hp.getProperty("ode.a.property.beginning.with.the.prefix.but.no.service"), "so green or red?"); + } + + 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(msg, hp.getProperty("ode.a.property.beginning.with.the.prefix.but.no.service"), "so green or red?"); + } + + 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"); + assertEquals("Number of properties is wrong", keys.size(), map.size()); + assertEquals("40000", map.get("timeout")); + assertEquals("30", map.get("max-redirects")); + assertEquals("so green or red?", map.get("ode.a.property.beginning.with.the.prefix.but.no.service")); + } + + + public void testCachedGetProperties() { + 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 { + File file = new File("/a-file-that-does-not-exist"); + Map m = new HierarchicalProperties(file).getProperties("an-uri", "a-service", "a-port"); + assertEquals("Should be empty", 0, m.size()); + } + +} diff --git a/utils/src/test/java/org/apache/ode/utils/HierarchiedPropertiesTest.java b/utils/src/test/java/org/apache/ode/utils/HierarchiedPropertiesTest.java deleted file mode 100644 index 4d5799b..0000000 --- a/utils/src/test/java/org/apache/ode/utils/HierarchiedPropertiesTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.ode.utils; - -import junit.framework.TestCase; - -import java.io.File; -import java.io.IOException; -import java.util.Map; -import java.util.List; -import java.util.Arrays; - -/** - * @author Alexis Midon - */ -public class HierarchiedPropertiesTest extends TestCase { - protected HierarchiedProperties hp; - - protected void setUp() throws Exception { - File file = new File(getClass().getResource("/hierarchied.properties").toURI()); - hp = new HierarchiedProperties(file); - } - - 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("brel-service", "max-redirects"), "40"); - assertEquals(msg, hp.getProperty("brel-service", "port-of-amsterdam", "max-redirects"), "60"); - assertEquals(msg, hp.getProperty("film-service", "timeout"), "40000"); - assertEquals(msg, hp.getProperty("film-service", "port-of-cannes", "timeout"), "50000"); - assertEquals(msg, hp.getProperty("brel-service", "port-of-amsterdam", "timeout"), "40000"); - assertEquals(msg, hp.getProperty("ode.a.property.beginning.with.the.prefix.but.no.service"), "so green or red?"); - } - - 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("film-service"); - assertEquals("Number of properties is wrong",keys.size(), map.size()); - assertEquals("40000", map.get("timeout")); - assertEquals("30", map.get("max-redirects")); - assertEquals("so green or red?", map.get("ode.a.property.beginning.with.the.prefix.but.no.service")); - } - - - public void testCachedGetProperties(){ - assertSame("Snapshot maps should be cached!", hp.getProperties("film-service"), hp.getProperties("film-service")); - assertSame("Snapshot maps should be cached!", hp.getProperties("film-service", "port-of-cannes"), hp.getProperties("film-service", "port-of-cannes")); - assertSame("Snapshot maps should be cached!", hp.getProperties("unknown-service"), hp.getProperties("unknown-service")); - } - - public void testWithNoFile() throws IOException { - File file = new File("/a-file-that-does-not-exist"); - Map m = new HierarchiedProperties(file).getProperties("a-service", "a-port"); - assertEquals("Should be empty", 0, m.size()); - } - -} diff --git a/utils/src/test/resources/hierarchical.properties b/utils/src/test/resources/hierarchical.properties new file mode 100644 index 0000000..c3c8014 --- /dev/null +++ b/utils/src/test/resources/hierarchical.properties @@ -0,0 +1,12 @@ +alias.foo=http://foo.com +alias.bar=http://bar.com + + +timeout=40000 +max-redirects=30 + +foo.film-service.port-of-cannes.ode.timeout=50000 +bar.brel-service.ode.max-redirects=40 +bar.brel-service.port-of-amsterdam.ode.max-redirects=60 +ode.a.property.beginning.with.the.prefix.but.no.service=so green or red? + diff --git a/utils/src/test/resources/hierarchied.properties b/utils/src/test/resources/hierarchied.properties deleted file mode 100644 index d624cb0..0000000 --- a/utils/src/test/resources/hierarchied.properties +++ /dev/null @@ -1,10 +0,0 @@ - - -timeout=40000 -max-redirects=30 - -film-service.port-of-cannes.ode.timeout=50000 -brel-service.ode.max-redirects=40 -brel-service.port-of-amsterdam.ode.max-redirects=60 -ode.a.property.beginning.with.the.prefix.but.no.service=so green or red? -