diff --git a/Rakefile b/Rakefile index 76b29fd..fbddd10 100644 --- a/Rakefile +++ b/Rakefile @@ -132,7 +132,7 @@ define "ode" do define "axis2" do compile.with projects("bpel-api", "bpel-connector", "bpel-dao", "bpel-epr", "bpel-runtime", "scheduler-simple", "bpel-schemas", "bpel-store", "utils"), - AXIOM, AXIS2_ALL, COMMONS.logging, COMMONS.collections, COMMONS.httpclient, DERBY, GERONIMO.kernel, GERONIMO.transaction, + AXIOM, AXIS2_ALL, COMMONS.lang, COMMONS.logging, COMMONS.collections, COMMONS.httpclient, DERBY, GERONIMO.kernel, GERONIMO.transaction, JAVAX.activation, JAVAX.servlet, JAVAX.stream, JAVAX.transaction, JENCKS, WSDL4J, WS_COMMONS.xml_schema, XMLBEANS @@ -464,7 +464,7 @@ define "ode" do desc "ODE Utils" define "utils" do - compile.with COMMONS.logging, COMMONS.pool, COMMONS.httpclient, COMMONS.codec, LOG4J, XERCES, JAVAX.stream, WSDL4J + compile.with COMMONS.collections, COMMONS.logging, COMMONS.pool, COMMONS.httpclient, COMMONS.codec, LOG4J, XERCES, JAVAX.stream, WSDL4J test.exclude "*TestResources" package :jar end diff --git a/axis2-war/src/test/java/org/apache/ode/axis2/ServiceFaultCatchTest.java b/axis2-war/src/test/java/org/apache/ode/axis2/ServiceFaultCatchTest.java index d0070f5..8ba0a42 100644 --- a/axis2-war/src/test/java/org/apache/ode/axis2/ServiceFaultCatchTest.java +++ b/axis2-war/src/test/java/org/apache/ode/axis2/ServiceFaultCatchTest.java @@ -3,6 +3,7 @@ package org.apache.ode.axis2; /** * Tests that a fault thrown by a called service can be caught and is properly * structured so that an assign on a fault sub-element will succeed. + * * @author Matthieu Riou */ public class ServiceFaultCatchTest extends Axis2TestBase { diff --git a/axis2/src/main/java/org/apache/ode/axis2/AuthenticationHelper.java b/axis2/src/main/java/org/apache/ode/axis2/AuthenticationHelper.java deleted file mode 100644 index b1bf1a7..0000000 --- a/axis2/src/main/java/org/apache/ode/axis2/AuthenticationHelper.java +++ /dev/null @@ -1,102 +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.axis2; - -import org.apache.axis2.client.Options; -import org.apache.axis2.transport.http.HTTPConstants; -import org.apache.axis2.transport.http.HttpTransportProperties; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.ode.bpel.iapi.PartnerRoleMessageExchange; -import org.apache.ode.utils.DOMUtils; -import org.w3c.dom.Element; - -/** - * HTTP Authentication Helper - * - * @author Alex Boisvert - */ -public class AuthenticationHelper { - - private static final Log __log = LogFactory.getLog(AuthenticationHelper.class); - - private static final String AUTHENTICATE_ELEMENT = "authenticate"; - - private static final String AUTHENTICATION_NS = "urn:ode.apache.org/authentication"; - - private static final String USERNAME_ELEMENT = "username"; - - private static final String PASSWORD_ELEMENT = "password"; - - private static final String DOMAIN_ELEMENT = "domain"; - - private static final String REALM_ELEMENT = "realm"; - - private static final String TOKEN_ELEMENT = "token"; - - public static void setHttpAuthentication(PartnerRoleMessageExchange odeMex, Options options) { - Element msg = odeMex.getRequest().getMessage(); - if (msg != null) { - Element part = DOMUtils.getFirstChildElement(msg); - while (part != null) { - Element content = DOMUtils.getFirstChildElement(part); - if (content != null) { - if (AUTHENTICATION_NS.equals(content.getNamespaceURI()) && AUTHENTICATE_ELEMENT.equals(content.getLocalName())) { - setOptions(options, content); - msg.removeChild(part); - break; - } - } - part = DOMUtils.getNextSiblingElement(part); - } - } - } - - protected static void setOptions(Options options, Element auth) { - String username = null; - String password = null; - String domain = null; - String realm = null; - Element e = DOMUtils.getFirstChildElement(auth); - while (e != null) { - if (USERNAME_ELEMENT.equals(e.getLocalName())) { - username = DOMUtils.getTextContent(e); - } - if (PASSWORD_ELEMENT.equals(e.getLocalName())) { - password = DOMUtils.getTextContent(e); - } - if (DOMAIN_ELEMENT.equals(e.getLocalName())) { - domain = DOMUtils.getTextContent(e); - } - if (REALM_ELEMENT.equals(e.getLocalName())) { - realm = DOMUtils.getTextContent(e); - } - e = DOMUtils.getNextSiblingElement(e); - } - - HttpTransportProperties.Authenticator authenticator = new HttpTransportProperties.Authenticator(); - authenticator.setUsername(username); - authenticator.setPassword(password); - authenticator.setDomain(domain); - authenticator.setRealm(realm); - options.setProperty(HTTPConstants.AUTHENTICATE, authenticator); - } - -} diff --git a/axis2/src/main/java/org/apache/ode/axis2/BindingContextImpl.java b/axis2/src/main/java/org/apache/ode/axis2/BindingContextImpl.java index 8e8d964..c897323 100644 --- a/axis2/src/main/java/org/apache/ode/axis2/BindingContextImpl.java +++ b/axis2/src/main/java/org/apache/ode/axis2/BindingContextImpl.java @@ -78,7 +78,7 @@ public class BindingContextImpl implements BindingContext { throw new ContextException("Cannot find definition for service " + initialPartnerEndpoint.serviceName + " in the context of process "+processId); } - return _server.createExternalService(wsdl, initialPartnerEndpoint.serviceName, initialPartnerEndpoint.portName, pconf); + return _server.createExternalService(pconf, initialPartnerEndpoint.serviceName, initialPartnerEndpoint.portName); } } 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 e34b631..c53fee8 100644 --- a/axis2/src/main/java/org/apache/ode/axis2/ODEServer.java +++ b/axis2/src/main/java/org/apache/ode/axis2/ODEServer.java @@ -312,7 +312,7 @@ public class ODEServer { public ODEService createService(ProcessConf pconf, QName serviceName, String portName) throws AxisFault { destroyService(serviceName, portName); AxisService axisService = ODEAxisService.createService(_axisConfig, pconf, serviceName, portName); - ODEService odeService = new ODEService(axisService, pconf.getDefinitionForService(serviceName), serviceName, portName, _server, _txMgr); + ODEService odeService = new ODEService(axisService, pconf, serviceName, portName, _server, _txMgr); _services.put(serviceName, portName, odeService); @@ -328,18 +328,19 @@ public class ODEServer { return odeService; } - public ExternalService createExternalService(Definition def, QName serviceName, String portName, ProcessConf pconf) throws ContextException { + public ExternalService createExternalService(ProcessConf pconf, QName serviceName, String portName) throws ContextException { ExternalService extService = (ExternalService) _externalServices.get(serviceName); if (extService != null) return extService; + Definition def = pconf.getDefinitionForService(serviceName); try { if (WsdlUtils.useHTTPBinding(def, serviceName, portName)) { if(__log.isDebugEnabled())__log.debug("Creating HTTP-bound external service " + serviceName); - extService = new HttpExternalService(def, serviceName, portName, _executorService, _scheduler, _server); + extService = new HttpExternalService(pconf, serviceName, portName, _executorService, _scheduler, _server); } else if (WsdlUtils.useSOAPBinding(def, serviceName, portName)) { if(__log.isDebugEnabled())__log.debug("Creating SOAP-bound external service " + serviceName); - extService = new SoapExternalService(def, serviceName, portName, _executorService, _axisConfig, _scheduler, _server, pconf); + extService = new SoapExternalService(pconf, serviceName, portName, _executorService, _axisConfig, _scheduler, _server); } } catch (Exception ex) { __log.error("Could not create external service.", ex); 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 76b0688..60f0403 100644 --- a/axis2/src/main/java/org/apache/ode/axis2/ODEService.java +++ b/axis2/src/main/java/org/apache/ode/axis2/ODEService.java @@ -36,6 +36,7 @@ import org.apache.ode.bpel.iapi.EndpointReference; import org.apache.ode.bpel.iapi.Message; import org.apache.ode.bpel.iapi.MessageExchange; import org.apache.ode.bpel.iapi.MyRoleMessageExchange; +import org.apache.ode.bpel.iapi.ProcessConf; import org.apache.ode.utils.DOMUtils; import org.apache.ode.utils.GUID; import org.apache.ode.utils.Namespaces; @@ -56,32 +57,34 @@ import java.util.concurrent.TimeUnit; /** * A running service, encapsulates the Axis service, its receivers and our * receivers as well. + * * @author Matthieu Riou */ public class ODEService { private static final Log __log = LogFactory.getLog(ODEService.class); - public static final int TIMEOUT = 2 * 60 * 1000; private AxisService _axisService; private BpelServer _server; private TransactionManager _txManager; + private ProcessConf _pconf; private Definition _wsdlDef; private QName _serviceName; private String _portName; private WSAEndpoint _serviceRef; private SoapMessageConverter _converter; - public ODEService(AxisService axisService, Definition def, QName serviceName, String portName, BpelServer server, + public ODEService(AxisService axisService, ProcessConf pconf, QName serviceName, String portName, BpelServer server, TransactionManager txManager) throws AxisFault { _axisService = axisService; _server = server; _txManager = txManager; - _wsdlDef = def; + _pconf = pconf; + _wsdlDef = pconf.getDefinitionForService(serviceName); _serviceName = serviceName; _portName = portName; _serviceRef = EndpointFactory.convertToWSA(createServiceRef(genEPRfromWSDL(_wsdlDef, serviceName, portName))); - _converter = new SoapMessageConverter(def, serviceName, portName); + _converter = new SoapMessageConverter(_wsdlDef, serviceName, portName); } @@ -143,7 +146,7 @@ public class ODEService { if (odeMex.getOperation().getOutput() != null) { // Waits for the response to arrive try { - responseFuture.get(TIMEOUT, TimeUnit.MILLISECONDS); + responseFuture.get(getTimeout(), TimeUnit.MILLISECONDS); } catch (Exception e) { String errorMsg = "Timeout or execution error when waiting for response to MEX " + odeMex + " " + e.toString(); @@ -211,6 +214,21 @@ public class ODEService { return result; } + /** + * 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); + if (timeout != null) { + try { + return Long.parseLong(timeout); + } catch (NumberFormatException e) { + if(__log.isWarnEnabled()) __log.warn("Mal-formatted Property: ["+ Properties.PROP_MEX_TIMEOUT+"="+timeout+"] Default value ("+Properties.DEFAULT_MEX_TIMEOUT+") will be used"); + } + } + return Properties.DEFAULT_MEX_TIMEOUT; + } + private void onResponse(MyRoleMessageExchange mex, MessageContext msgContext) throws AxisFault { switch (mex.getStatus()) { case FAULT: @@ -276,7 +294,7 @@ public class ODEService { // The callback endpoint is going to be the same as the target // endpoint in this case, except that it is updated with session // information (if available). - if (odeMex.getProperty(MessageExchange.PROPERTY_SEP_MYROLE_SESSIONID)!= null) { + if (odeMex.getProperty(MessageExchange.PROPERTY_SEP_MYROLE_SESSIONID) != null) { _serviceRef.setSessionId(odeMex.getProperty(MessageExchange.PROPERTY_SEP_MYROLE_SESSIONID)); msgContext.setProperty("callbackSessionEndpoint", _serviceRef); } @@ -290,6 +308,7 @@ public class ODEService { /** * Return the service-ref element that will be used to represent this * endpoint. + * * @return my service endpoint */ public EndpointReference getMyServiceRef() { @@ -299,10 +318,8 @@ public class ODEService { /** * Get the EPR of this service from the WSDL. * - * @param name - * service name - * @param portName - * port name + * @param name service name + * @param portName port name * @return XML representation of the EPR */ public static Element genEPRfromWSDL(Definition wsdlDef, QName name, String portName) { diff --git a/axis2/src/main/java/org/apache/ode/axis2/Properties.java b/axis2/src/main/java/org/apache/ode/axis2/Properties.java new file mode 100644 index 0000000..f52057a --- /dev/null +++ b/axis2/src/main/java/org/apache/ode/axis2/Properties.java @@ -0,0 +1,247 @@ +/* + * 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.axis2; + +import org.apache.axis2.Constants; +import org.apache.axis2.client.Options; +import org.apache.axis2.transport.http.HTTPConstants; +import org.apache.axis2.transport.http.HttpTransportProperties; +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpVersion; +import org.apache.commons.httpclient.ProtocolException; +import org.apache.commons.httpclient.params.DefaultHttpParams; +import org.apache.commons.httpclient.params.HttpConnectionParams; +import org.apache.commons.httpclient.params.HttpMethodParams; +import org.apache.commons.httpclient.params.HttpParams; +import org.apache.commons.httpclient.params.HttpClientParams; +import org.apache.commons.httpclient.params.HostParams; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.Map; +import java.util.List; +import java.util.Collection; + +/** + * @author Alexis Midon + */ +public class Properties { + + /** + * Property used to define how long (in miiliseconds) the message will wait for a response. Default value is {@link #DEFAULT_MEX_TIMEOUT} + */ + public static final String PROP_MEX_TIMEOUT = "mex.timeout"; + // its default value + public static final int DEFAULT_MEX_TIMEOUT = 2 * 60 * 1000; + + + public static final String PROP_HTTP_CONNECTION_TIMEOUT = HttpConnectionParams.CONNECTION_TIMEOUT; + public static final String PROP_HTTP_SOCKET_TIMEOUT = HttpMethodParams.SO_TIMEOUT; + public static final String PROP_HTTP_PROTOCOL_VERSION = HttpMethodParams.PROTOCOL_VERSION; + public static final String PROP_HTTP_HEADER_PREFIX = "http.default-headers."; + public static final String PROP_HTTP_PROXY_PREFIX = "http.proxy."; + public static final String PROP_HTTP_PROXY_HOST = PROP_HTTP_PROXY_PREFIX + "host"; + public static final String PROP_HTTP_PROXY_PORT = PROP_HTTP_PROXY_PREFIX + "port"; + public static final String PROP_HTTP_PROXY_DOMAIN = PROP_HTTP_PROXY_PREFIX + "domain"; + public static final String PROP_HTTP_PROXY_USER = PROP_HTTP_PROXY_PREFIX + "user"; + public static final String PROP_HTTP_PROXY_PASSWORD = PROP_HTTP_PROXY_PREFIX + "password"; + + // Httpclient specific + public static final String PROP_HTTP_MAX_REDIRECTS = HttpClientParams.MAX_REDIRECTS; + + // Axis2-specific + public static final String PROP_HTTP_PROTOCOL_ENCODING = "http.protocol.encoding"; + public static final String PROP_HTTP_REQUEST_CHUNK = "http.request.chunk"; + public static final String PROP_HTTP_REQUEST_GZIP = "http.request.gzip"; + public static final String PROP_HTTP_ACCEPT_GZIP = "http.accept.gzip"; + + + protected static final Log log = LogFactory.getLog(Properties.class); + + public static Object[] getProxyAndHeaders(Map properties) { + ArrayList
headers = null; // /!\ Axis2 requires an ArrayList (not a List implementation) + HttpTransportProperties.ProxyProperties proxy = null; + for (Map.Entry e : properties.entrySet()) { + final String k = e.getKey(); + final String v = e.getValue(); + if (k.startsWith(PROP_HTTP_HEADER_PREFIX)) { + if (headers == null) headers = new ArrayList
(); + // extract the header name + String name = k.substring(PROP_HTTP_HEADER_PREFIX.length()); + headers.add(new Header(name, v)); + } else if (k.startsWith(PROP_HTTP_PROXY_PREFIX)) { + if (proxy == null) proxy = new HttpTransportProperties.ProxyProperties(); + + if (PROP_HTTP_PROXY_HOST.equals(k)) proxy.setProxyName(v); + else if (PROP_HTTP_PROXY_PORT.equals(k)) proxy.setProxyPort(Integer.parseInt(v)); + else if (PROP_HTTP_PROXY_DOMAIN.equals(k)) proxy.setDomain(v); + else if (PROP_HTTP_PROXY_USER.equals(k)) proxy.setUserName(v); + else if (PROP_HTTP_PROXY_PASSWORD.equals(k)) proxy.setPassWord(v); + else if (log.isWarnEnabled()) + log.warn("Unknown proxy properties [" + k + "]. " + PROP_HTTP_PROXY_PREFIX + " is a refix reserved for proxy properties."); + } + } + if (proxy != null) { + String host = proxy.getProxyHostName(); + if (host == null || host.length() == 0) { + // disable proxy if the host is not null + proxy=null; + if(log.isDebugEnabled()) log.debug("Proxy host is null. Proxy will not be taken into account."); + } + } + + return new Object[]{proxy, headers}; + } + + public static class Axis2 { + + public static Options translate(Map properties) { + return translate(properties, new Options()); + } + + public static Options translate(Map properties, Options options) { + if (log.isDebugEnabled()) log.debug("Translating IL Properties for Axis2"); + if (properties.isEmpty()) return options; + if (properties.containsKey(PROP_HTTP_CONNECTION_TIMEOUT)) { + final String value = properties.get(PROP_HTTP_CONNECTION_TIMEOUT); + try { + options.setProperty(HTTPConstants.CONNECTION_TIMEOUT, Integer.valueOf(value)); + } catch (NumberFormatException e) { + if (log.isWarnEnabled()) + log.warn("Mal-formatted Property: [" + Properties.PROP_HTTP_CONNECTION_TIMEOUT + "=" + value + "] Property will be skipped."); + } + } + if (properties.containsKey(PROP_HTTP_SOCKET_TIMEOUT)) { + final String value = properties.get(PROP_HTTP_SOCKET_TIMEOUT); + try { + options.setProperty(HTTPConstants.SO_TIMEOUT, Integer.valueOf(value)); + } catch (NumberFormatException e) { + if (log.isWarnEnabled()) + log.warn("Mal-formatted Property: [" + Properties.PROP_HTTP_SOCKET_TIMEOUT + "=" + value + "] Property will be skipped."); + } + } + if (properties.containsKey(PROP_HTTP_PROTOCOL_ENCODING)) { + options.setProperty(Constants.Configuration.CHARACTER_SET_ENCODING, properties.get(PROP_HTTP_PROTOCOL_ENCODING)); + } + if (properties.containsKey(PROP_HTTP_PROTOCOL_VERSION)) { + options.setProperty(HTTPConstants.HTTP_PROTOCOL_VERSION, properties.get(PROP_HTTP_PROTOCOL_VERSION)); + } + if (properties.containsKey(PROP_HTTP_REQUEST_CHUNK)) { + options.setProperty(HTTPConstants.CHUNKED, properties.get(PROP_HTTP_REQUEST_CHUNK)); + } + if (properties.containsKey(PROP_HTTP_REQUEST_GZIP)) { + options.setProperty(HTTPConstants.MC_GZIP_REQUEST, properties.get(PROP_HTTP_REQUEST_GZIP)); + } + if (properties.containsKey(PROP_HTTP_ACCEPT_GZIP)) { + options.setProperty(HTTPConstants.MC_ACCEPT_GZIP, properties.get(PROP_HTTP_ACCEPT_GZIP)); + } + if (properties.containsKey(PROP_HTTP_MAX_REDIRECTS)) { + if (log.isWarnEnabled()) log.warn("Property Not Supported: " + PROP_HTTP_MAX_REDIRECTS); + } + + // iterate through the properties to get Headers & Proxy information + Object[] o = getProxyAndHeaders(properties); + HttpTransportProperties.ProxyProperties proxy = (HttpTransportProperties.ProxyProperties) o[0]; + ArrayList
headers = (ArrayList
) o[1]; // /!\ Axis2 requires an ArrayList (not a List implementation) + if (headers != null && !headers.isEmpty()) options.setProperty(HTTPConstants.HTTP_HEADERS, headers); + if (proxy != null) options.setProperty(HTTPConstants.PROXY, proxy); + + return options; + } + } + + + public static class HttpClient { + public static HttpParams translate(Map properties) { + return translate(properties, new DefaultHttpParams()); + } + + public static HttpParams translate(Map properties, HttpParams p) { + if (log.isDebugEnabled()) log.debug("Translating IL Properties for HttpClient. Properties size="+properties.size()); + if (properties.isEmpty()) return p; + + // initialize the collection of headers + p.setParameter(HostParams.DEFAULT_HEADERS, new ArrayList()); + + if (properties.containsKey(PROP_HTTP_CONNECTION_TIMEOUT)) { + final String value = properties.get(PROP_HTTP_CONNECTION_TIMEOUT); + try { + p.setParameter(HttpConnectionParams.CONNECTION_TIMEOUT, Integer.valueOf(value)); + } catch (NumberFormatException e) { + if (log.isWarnEnabled()) + log.warn("Mal-formatted Property: [" + Properties.PROP_HTTP_CONNECTION_TIMEOUT + "=" + value + "] Property will be skipped."); + } + } + if (properties.containsKey(PROP_HTTP_SOCKET_TIMEOUT)) { + final String value = properties.get(PROP_HTTP_SOCKET_TIMEOUT); + try { + p.setParameter(HttpMethodParams.SO_TIMEOUT, Integer.valueOf(value)); + } catch (NumberFormatException e) { + if (log.isWarnEnabled()) + log.warn("Mal-formatted Property: [" + Properties.PROP_HTTP_SOCKET_TIMEOUT + "=" + value + "] Property will be skipped."); + } + } + if (properties.containsKey(PROP_HTTP_PROTOCOL_ENCODING)) { + p.setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, properties.get(PROP_HTTP_PROTOCOL_ENCODING)); + } + if (properties.containsKey(PROP_HTTP_PROTOCOL_VERSION)) { + try { + p.setParameter(HttpMethodParams.PROTOCOL_VERSION, HttpVersion.parse(properties.get(PROP_HTTP_PROTOCOL_VERSION))); + } catch (ProtocolException e) { + if (log.isWarnEnabled()) + log.warn("Mal-formatted Property: [" + PROP_HTTP_PROTOCOL_VERSION + "]", e); + } + } + if (properties.containsKey(PROP_HTTP_REQUEST_CHUNK)) { + // see org.apache.commons.httpclient.methods.EntityEnclosingMethod.setContentChunked() + p.setBooleanParameter(PROP_HTTP_REQUEST_CHUNK, Boolean.parseBoolean(properties.get(PROP_HTTP_REQUEST_CHUNK))); + } + if (properties.containsKey(PROP_HTTP_REQUEST_GZIP)) { + if (log.isWarnEnabled()) log.warn("Property Not Supported: " + PROP_HTTP_REQUEST_GZIP); + } + + if (Boolean.parseBoolean(properties.get(PROP_HTTP_ACCEPT_GZIP))) { + // append gzip to the list of accepted encoding + ((Collection) p.getParameter(HostParams.DEFAULT_HEADERS)).add(new Header("Accept-Encoding", "gzip")); + } + + if (properties.containsKey(PROP_HTTP_MAX_REDIRECTS)) { + final String value = properties.get(PROP_HTTP_MAX_REDIRECTS); + try { + p.setParameter(HttpClientParams.MAX_REDIRECTS, Integer.valueOf(value)); + } catch (NumberFormatException e) { + if (log.isWarnEnabled()) + log.warn("Mal-formatted Property: [" + Properties.PROP_HTTP_MAX_REDIRECTS + "=" + value + "] Property will be skipped."); + } + } + + Object[] o = getProxyAndHeaders(properties); + HttpTransportProperties.ProxyProperties proxy = (HttpTransportProperties.ProxyProperties) o[0]; + Collection headers = (Collection) o[1]; + if (headers != null && !headers.isEmpty()) + ((Collection) p.getParameter(HostParams.DEFAULT_HEADERS)).addAll(headers); + if (proxy != null) p.setParameter(PROP_HTTP_PROXY_PREFIX, proxy); + + return p; + } + + } +} 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 fdd40e4..402ce65 100644 --- a/axis2/src/main/java/org/apache/ode/axis2/SoapExternalService.java +++ b/axis2/src/main/java/org/apache/ode/axis2/SoapExternalService.java @@ -21,6 +21,7 @@ package org.apache.ode.axis2; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; +import java.util.Map; import java.io.File; import java.io.InputStream; @@ -56,6 +57,8 @@ import org.apache.ode.bpel.iapi.MessageExchange.FailureType; import org.apache.ode.il.OMUtils; import org.apache.ode.utils.DOMUtils; import org.apache.ode.utils.Namespaces; +import org.apache.ode.utils.WatchDog; +import org.apache.ode.utils.CollectionUtils; import org.apache.ode.utils.fs.FileWatchDog; import org.apache.ode.utils.wsdl.Messages; import org.apache.ode.utils.uuid.UUID; @@ -77,6 +80,7 @@ public class SoapExternalService implements ExternalService { private static final int EXPIRE_SERVICE_CLIENT = 30000; + private static ThreadLocal _cachedOptions = new ThreadLocal(); private static ThreadLocal _cachedClients = new ThreadLocal(); private ExecutorService _executorService; @@ -89,18 +93,17 @@ public class SoapExternalService implements ExternalService { private BpelServer _server; private ProcessConf _pconf; - public SoapExternalService(Definition definition, QName serviceName, String portName, ExecutorService executorService, - AxisConfiguration axisConfig, Scheduler sched, BpelServer server, ProcessConf pconf) throws AxisFault { - _definition = definition; + public SoapExternalService(ProcessConf pconf, QName serviceName, String portName, ExecutorService executorService, + AxisConfiguration axisConfig, Scheduler sched, BpelServer server) throws AxisFault { + _definition = pconf.getDefinitionForService(serviceName); _serviceName = serviceName; _portName = portName; _executorService = executorService; _axisConfig = axisConfig; _sched = sched; - _converter = new SoapMessageConverter(definition, serviceName, portName); + _converter = new SoapMessageConverter(_definition, serviceName, portName); _server = server; _pconf = pconf; - } public void invoke(final PartnerRoleMessageExchange odeMex) { @@ -121,20 +124,16 @@ public class SoapExternalService implements ExternalService { __log.debug("Message: " + soapEnv); } - Options options = new Options(); - options.setAction(mctx.getSoapAction()); - options.setTo(axisEPR); - options.setTimeOutInMilliSeconds(60000); - options.setExceptionToBeThrownOnSOAPFault(false); - - AuthenticationHelper.setHttpAuthentication(odeMex, options); - - CachedServiceClient cached = getCachedServiceClient(); - final OperationClient operationClient = cached._client.createClient(isTwoWay ? ServiceClient.ANON_OUT_IN_OP + ServiceClient client = getCachedServiceClient().client; + final OperationClient operationClient = client.createClient(isTwoWay ? ServiceClient.ANON_OUT_IN_OP : ServiceClient.ANON_OUT_ONLY_OP); - operationClient.setOptions(options); operationClient.addMessageContext(mctx); + // this Options can be alter without impacting the ServiceClient options (which is a requirement) + Options operationOptions = operationClient.getOptions(); + operationOptions.setAction(mctx.getSoapAction()); + operationOptions.setTo(axisEPR); + if (isTwoWay) { final String mexId = odeMex.getMessageExchangeId(); @@ -194,19 +193,29 @@ public class SoapExternalService implements ExternalService { } private CachedServiceClient getCachedServiceClient() throws AxisFault { - CachedServiceClient cached = _cachedClients.get(); - if (cached == null) { - cached = new CachedServiceClient(new File(_pconf.getBaseURI().resolve(_serviceName.getLocalPart() + ".axis2")), EXPIRE_SERVICE_CLIENT); - _cachedClients.set(cached); + CachedServiceClient cachedServiceClient = _cachedClients.get(); + if (cachedServiceClient == null) { + cachedServiceClient = new CachedServiceClient(new File(_pconf.getBaseURI().resolve(_serviceName.getLocalPart() + ".axis2")), EXPIRE_SERVICE_CLIENT); + _cachedClients.set(cachedServiceClient); } try { // call manually the check procedure // we dont want a dedicated thread for that - cached.checkAndConfigure(); - } catch (Exception e) { - throw AxisFault.makeFault(e); + cachedServiceClient.check(); + } catch (RuntimeException e) { + throw AxisFault.makeFault(e.getCause() != null ? e.getCause() : e); + } + + SoapExternalService.CachedOptions cachedOptions = _cachedOptions.get(); + if (cachedOptions == null) { + cachedOptions = new CachedOptions(); + _cachedOptions.set(cachedOptions); } - return cached; + cachedOptions.check(); + + // apply the options to the service client + cachedServiceClient.client.setOptions(cachedOptions.options); + return cachedServiceClient; } /** @@ -369,35 +378,39 @@ public class SoapExternalService implements ExternalService { /** * This class wraps a {@link org.apache.axis2.client.ServiceClient} and watches changes (deletions,creations,updates) - * on a Axis2 service config file named {service-name}.axis2.

+ * on a Axis2 service config file named {service-name}.axis2.

* The {@link org.apache.axis2.client.ServiceClient} instance is created from the main Axis2 config instance and - * this service-specific config file. + * this service-specific config file. */ - class CachedServiceClient extends FileWatchDog { - ServiceClient _client; + private class CachedServiceClient extends FileWatchDog { + ServiceClient client; protected CachedServiceClient(File file, long delay) { super(file, delay); } - protected boolean isInitialized() throws Exception { - return _client != null; + protected boolean isInitialized() { + return client != null; } - protected void init() throws Exception { - _client = new ServiceClient(new ConfigurationContext(_axisConfig), null); + protected void init() { + try { + client = new ServiceClient(new ConfigurationContext(_axisConfig), null); + } catch (AxisFault axisFault) { + throw new RuntimeException(axisFault); + } } - protected void doOnUpdate() throws Exception { + protected void doOnUpdate() { // axis2 service configuration // if the config file has been modified (i.e added or updated), re-create a ServiceClient // and load the new config. - init(); //reset the ServiceClient instance + init(); // create a new ServiceClient instance try { InputStream ais = file.toURI().toURL().openStream(); if (ais != null) { if (__log.isDebugEnabled()) __log.debug("Configuring service " + _serviceName + " using: " + file); - ServiceBuilder builder = new ServiceBuilder(ais, new ConfigurationContext(_client.getAxisConfiguration()), _client.getAxisService()); + ServiceBuilder builder = new ServiceBuilder(ais, new ConfigurationContext(client.getAxisConfiguration()), client.getAxisService()); builder.populateService(builder.buildOM()); } } catch (Exception e) { @@ -406,4 +419,50 @@ public class SoapExternalService implements ExternalService { } } + private class CachedOptions extends WatchDog { + + Options options; + + private CachedOptions() { + super(new WatchDog.Mutable() { + // ProcessConf#getProperties(String...) cannot return ull (by contract) + public boolean exists() { + return true; + } + + public boolean hasChangedSince(Map since) { + Map latest = lastModified(); // cannot be null but better be prepared + // check if mappings are equal + return !CollectionUtils.equals(latest, since); + } + + public Map lastModified() { + return _pconf.getProperties(_serviceName.getLocalPart(), _portName); + } + }); + } + + protected boolean isInitialized() { + return options != null; + } + + protected void init() { + options = new Options(); + } + + protected void doOnUpdate() { + init(); + Map properties = _pconf.getProperties(_serviceName.getLocalPart(), _portName); + Properties.Axis2.translate(properties, options); + + // set defaults values + options.setExceptionToBeThrownOnSOAPFault(false); + + // this value does NOT override Properties.PROP_HTTP_CONNECTION_TIMEOUT + // nor Properties.PROP_HTTP_SOCKET_TIMEOUT. + // it will be applied only if the laters are not set. + options.setTimeOutInMilliSeconds(60000); + } + } + } diff --git a/axis2/src/main/java/org/apache/ode/axis2/httpbinding/HttpClientHelper.java b/axis2/src/main/java/org/apache/ode/axis2/httpbinding/HttpClientHelper.java new file mode 100644 index 0000000..ca78d35 --- /dev/null +++ b/axis2/src/main/java/org/apache/ode/axis2/httpbinding/HttpClientHelper.java @@ -0,0 +1,229 @@ +/* + * 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.axis2.httpbinding; + +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HostConfiguration; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.HttpState; +import org.apache.commons.httpclient.URI; +import org.apache.commons.httpclient.URIException; +import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; +import org.apache.commons.httpclient.methods.DeleteMethod; +import org.apache.commons.httpclient.methods.EntityEnclosingMethod; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.methods.RequestEntity; +import org.apache.commons.httpclient.methods.StringRequestEntity; +import org.apache.commons.httpclient.params.HttpParams; +import org.apache.commons.httpclient.params.HostParams; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.ode.axis2.Properties; +import org.apache.ode.axis2.util.URLEncodedTransformer; +import org.apache.ode.axis2.util.UrlReplacementTransformer; +import org.apache.ode.bpel.epr.MutableEndpoint; +import org.apache.ode.bpel.iapi.PartnerRoleMessageExchange; +import org.apache.ode.utils.DOMUtils; +import org.apache.ode.utils.wsdl.Messages; +import org.apache.ode.utils.wsdl.WsdlUtils; +import org.apache.axis2.transport.http.HttpTransportProperties; +import org.w3c.dom.Element; + +import javax.wsdl.Binding; +import javax.wsdl.BindingInput; +import javax.wsdl.BindingOperation; +import javax.wsdl.Message; +import javax.wsdl.Operation; +import javax.wsdl.Part; +import javax.wsdl.extensions.http.HTTPOperation; +import javax.xml.namespace.QName; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Collection; + +public class HttpClientHelper { + + private static final String CONTENT_TYPE_TEXT_XML = "text/xml"; + private static final Log log = LogFactory.getLog(HttpClientHelper.class); + + protected static final org.apache.ode.utils.wsdl.Messages msgs = Messages.getMessages(Messages.class); + protected Binding binding; + + public HttpClientHelper(Binding binding) { + this.binding = binding; + } + + public void configure(HostConfiguration hostConfig, HttpState state, URI targetURI, HttpParams params) throws URIException { + if (log.isDebugEnabled()) log.debug("Configuring http client..."); + // proxy configuration + if (ProxyConf.isProxyEnabled(params, targetURI.getHost())) { + if (log.isDebugEnabled()) log.debug("ProxyConf"); + ProxyConf.configure(hostConfig, state, (HttpTransportProperties.ProxyProperties) params.getParameter(Properties.PROP_HTTP_PROXY_PREFIX)); + } + + // security + // ... + + } + + public HttpMethod buildHttpMethod(PartnerRoleMessageExchange odeMex, HttpParams params) throws UnsupportedEncodingException { + Operation operation = odeMex.getOperation(); + BindingOperation bindingOperation = binding.getBindingOperation(operation.getName(), operation.getInput().getName(), operation.getOutput().getName()); + + // message to be sent + Element message = odeMex.getRequest().getMessage(); + Message msgDef = operation.getInput().getMessage(); + + // base url + String url = ((MutableEndpoint) odeMex.getEndpointReference()).getUrl(); + + // extract part values into a map and check that all parts are assigned a value + Map partElements = extractPartElements(msgDef, message); + + // http method type + // the operation may override the verb, this is an extension for RESTful BPEL + String verb = WsdlUtils.resolveVerb(binding, bindingOperation); + + // build the http method itself + HttpMethod method = prepareHttpMethod(bindingOperation, verb, partElements, url, params); + return method; + } + + protected Map extractPartElements(Message msgDef, Element message) { + Map partValues = new HashMap(); + for (Iterator iterator = msgDef.getParts().values().iterator(); iterator.hasNext();) { + Part part = (Part) iterator.next(); + Element partEl = DOMUtils.findChildByName(message, new QName(null, part.getName())); + if (partEl == null) + throw new IllegalArgumentException(msgs.msgOdeMessageMissingRequiredPart(part.getName())); + partValues.put(part.getName(), partEl); + } + return partValues; + } + + /** + * create and initialize the http method. + * Http Headers that may been passed in the params are not set in this method. + * Headers will be automatically set by HttpClient. + * See usages of HostParams.DEFAULT_HEADERS + * See org.apache.commons.httpclient.HttpMethodDirector#executeMethod(org.apache.commons.httpclient.HttpMethod) + */ + protected HttpMethod prepareHttpMethod(BindingOperation bindingOperation, String verb, Map partValues, + final String rootUri, HttpParams params) throws UnsupportedEncodingException { + if (log.isDebugEnabled()) log.debug("Preparing http request..."); + // convenience variables... + BindingInput bindingInput = bindingOperation.getBindingInput(); + HTTPOperation httpOperation = (HTTPOperation) WsdlUtils.getOperationExtension(bindingOperation); + String contentType = WsdlUtils.getMimeContentType(bindingInput.getExtensibilityElements()); + boolean useUrlEncoded = WsdlUtils.useUrlEncoded(bindingInput) || PostMethod.FORM_URL_ENCODED_CONTENT_TYPE.equalsIgnoreCase(contentType); + boolean useUrlReplacement = WsdlUtils.useUrlReplacement(bindingInput); + + final UrlReplacementTransformer replacementTransformer = new UrlReplacementTransformer(partValues.keySet()); + final URLEncodedTransformer encodedTransformer = new URLEncodedTransformer(); + + // the http method to be built and returned + HttpMethod method = null; + // the 4 elements the http method may be made of + String relativeUri = httpOperation.getLocationURI(); + String queryPath = null; + RequestEntity requestEntity = null; + String encodedParams = null; + + + if (useUrlReplacement) { + // insert part values in the url + relativeUri = replacementTransformer.transform(relativeUri, partValues); + } else if (useUrlEncoded) { + // encode part values + encodedParams = encodedTransformer.transform(partValues); + } + + // http-client api is not really neat + // something similar to the following would save some if/else manipulations. + // But we have to deal with it as-is. + // + // method = new Method(verb); + // method.setRequestEnity(..) + // etc... + if ("GET".equalsIgnoreCase(verb) || "DELETE".equalsIgnoreCase(verb)) { + if ("GET".equalsIgnoreCase(verb)) { + method = new GetMethod(); + } else if ("DELETE".equalsIgnoreCase(verb)) { + method = new DeleteMethod(); + } + if (useUrlEncoded) { + queryPath = encodedParams; + } + + // Let http-client manage the redirection + // see org.apache.commons.httpclient.params.HttpClientParams.MAX_REDIRECTS + // default is 100 + method.setFollowRedirects(true); + } else if ("POST".equalsIgnoreCase(verb) || "PUT".equalsIgnoreCase(verb)) { + + if ("POST".equalsIgnoreCase(verb)) { + method = new PostMethod(); + } else if ("PUT".equalsIgnoreCase(verb)) { + method = new PutMethod(); + } + + // some body-building... + if (useUrlEncoded) { + requestEntity = new StringRequestEntity(encodedParams, PostMethod.FORM_URL_ENCODED_CONTENT_TYPE, method.getParams().getContentCharset()); + } else if (contentType.endsWith(CONTENT_TYPE_TEXT_XML)) { + // assumption is made that there is a single part + // validation steps in the constructor must warranty that + Part part = (Part) bindingOperation.getOperation().getInput().getMessage().getParts().values().iterator().next(); + Element partValue = partValues.get(part.getName()); + // if the part has an element name, we must take the first element + if (part.getElementName() != null) { + partValue = DOMUtils.getFirstChildElement(partValue); + } + String xmlString = DOMUtils.domToString(partValue); + requestEntity = new ByteArrayRequestEntity(xmlString.getBytes(), contentType); + } else { + // should not happen because of HttpBindingValidator, but never say never + throw new IllegalArgumentException("Unsupported content-type!"); + } + + // cast safely, PUT and POST are subclasses of EntityEnclosingMethod + final EntityEnclosingMethod enclosingMethod = (EntityEnclosingMethod) method; + enclosingMethod.setRequestEntity(requestEntity); + enclosingMethod.setContentChunked(params.getBooleanParameter(Properties.PROP_HTTP_REQUEST_CHUNK, false)); + + } else { + // should not happen because of HttpBindingValidator, but never say never + throw new IllegalArgumentException("Unsupported HTTP method: " + verb); + } + + // link params together + method.getParams().setDefaults(params); + + String completeUri = rootUri + (rootUri.endsWith("/") || relativeUri.startsWith("/") ? "" : "/") + relativeUri; + method.setPath(completeUri); // assumes that the path is properly encoded (URL safe). + method.setQueryString(queryPath); + return method; + } +} 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 556eb12..34584a1 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 @@ -19,20 +19,24 @@ package org.apache.ode.axis2.httpbinding; -import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; 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; 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.iapi.BpelServer; import org.apache.ode.bpel.iapi.EndpointReference; import org.apache.ode.bpel.iapi.MessageExchange; import org.apache.ode.bpel.iapi.PartnerRoleMessageExchange; +import org.apache.ode.bpel.iapi.ProcessConf; import org.apache.ode.bpel.iapi.Scheduler; import org.apache.ode.utils.DOMUtils; import org.apache.ode.utils.Namespaces; @@ -52,6 +56,7 @@ import javax.xml.namespace.QName; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; @@ -64,24 +69,26 @@ public class HttpExternalService implements ExternalService { private static final Log log = LogFactory.getLog(HttpExternalService.class); private static final Messages msgs = Messages.getMessages(Messages.class); - private MultiThreadedHttpConnectionManager connections = new MultiThreadedHttpConnectionManager(); + private MultiThreadedHttpConnectionManager connections; protected ExecutorService executorService; protected Scheduler scheduler; protected BpelServer server; + protected ProcessConf pconf; protected QName serviceName; protected String portName; - protected HttpMethodBuilder methodBuilder; + protected HttpClientHelper clientHelper; protected WSAEndpoint endpointReference; - public HttpExternalService(Definition definition, QName serviceName, String portName, ExecutorService executorService, Scheduler scheduler, BpelServer server) { + public HttpExternalService(ProcessConf pconf, QName serviceName, String portName, ExecutorService executorService, Scheduler scheduler, BpelServer server) { this.portName = portName; this.serviceName = serviceName; this.executorService = executorService; this.scheduler = scheduler; this.server = server; - + this.pconf = pconf; + Definition definition = pconf.getDefinitionForService(serviceName); Service serviceDef = definition.getService(serviceName); if (serviceDef == null) throw new IllegalArgumentException(msgs.msgServiceDefinitionNotFound(serviceName)); @@ -105,7 +112,8 @@ public class HttpExternalService implements ExternalService { throw new IllegalArgumentException(msgs.msgPortDefinitionNotFound(serviceName, portName)); endpointReference = EndpointFactory.convertToWSA(ODEService.createServiceRef(eprElmt)); - methodBuilder = new HttpMethodBuilder(binding); + clientHelper = new HttpClientHelper(binding); + connections = new MultiThreadedHttpConnectionManager(); } public String getPortName() { @@ -127,16 +135,28 @@ 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); + final HttpParams params = Properties.HttpClient.translate(properties); + // build the http method - final HttpMethod method = methodBuilder.buildHttpMethod(odeMex); - // this callable encapsulate the http method execution and the process of the response + final HttpMethod method = clientHelper.buildHttpMethod(odeMex, params); + + // create a client + HttpClient client = new HttpClient(connections); + // don't forget to wire params so that IL properties are passed around + client.getParams().setDefaults(params); + + clientHelper.configure(client.getHostConfiguration(), client.getState(), method.getURI(), params); + + // this callable encapsulates the http method execution and the process of the response final Callable executionCallable; // execute it boolean isTwoWay = odeMex.getMessageExchangePattern() == org.apache.ode.bpel.iapi.MessageExchange.MessageExchangePattern.REQUEST_RESPONSE; if (isTwoWay) { // two way - executionCallable = new HttpExternalService.TwoWayCallable(method, odeMex.getMessageExchangeId(), odeMex.getOperation()); + executionCallable = new HttpExternalService.TwoWayCallable(client, method, odeMex.getMessageExchangeId(), odeMex.getOperation()); scheduler.registerSynchronizer(new Scheduler.Synchronizer() { public void afterCompletion(boolean success) { // If the TX is rolled back, then we don't send the request. @@ -151,36 +171,44 @@ public class HttpExternalService implements ExternalService { odeMex.replyAsync(); } else { // one way, just execute and forget - executionCallable = new HttpExternalService.OneWayCallable(method, odeMex.getMessageExchangeId(), odeMex.getOperation()); + executionCallable = new HttpExternalService.OneWayCallable(client, method, odeMex.getMessageExchangeId(), odeMex.getOperation()); executorService.submit(executionCallable); odeMex.replyOneWayOk(); } } catch (UnsupportedEncodingException e) { + String errmsg = "The HTTP encoding returned isn't supported " + odeMex; + log.error(errmsg, e); + odeMex.replyWithFailure(MessageExchange.FailureType.FORMAT_ERROR, errmsg, null); + } catch (URIException e) { String errmsg = "Error sending message to " + getClass().getSimpleName() + " for ODE mex " + odeMex; log.error(errmsg, e); odeMex.replyWithFailure(MessageExchange.FailureType.FORMAT_ERROR, errmsg, null); + } catch (Exception e) { + String errmsg = "Unknown HTTP call error for ODE mex " + odeMex; + log.error(errmsg, e); + odeMex.replyWithFailure(MessageExchange.FailureType.OTHER, errmsg, null); } - } - private class OneWayCallable implements Callable { HttpMethod method; String mexId; Operation operation; + HttpClient client; - public OneWayCallable(HttpMethod method, String mexId, Operation operation) { + public OneWayCallable(org.apache.commons.httpclient.HttpClient client, HttpMethod method, String mexId, Operation operation) { this.method = method; this.mexId = mexId; this.operation = operation; + this.client = client; } public Void call() throws Exception { try { // simply execute the http method - HttpClient client = new HttpClient(connections); if (log.isDebugEnabled()) log.debug("Executing http request : " + method.getName() + " " + method.getURI()); + final int statusCode = client.executeMethod(method); // invoke getResponseBody to force the loading of the body // Actually the processResponse may happen in a separate thread and @@ -215,19 +243,21 @@ public class HttpExternalService implements ExternalService { try { // log the URI since the engine may have moved on while this One Way request was executing if (statusCode >= 400) { - if (log.isWarnEnabled()) log.warn("OneWay http request ["+method.getURI()+"] failed with status: " + method.getStatusLine()); + if (log.isWarnEnabled()) + log.warn("OneWay http request [" + method.getURI() + "] failed with status: " + method.getStatusLine()); } else { - if (log.isDebugEnabled()) log.debug("OneWay http request ["+method.getURI()+"] status: " + method.getStatusLine()); + if (log.isDebugEnabled()) + log.debug("OneWay http request [" + method.getURI() + "] status: " + method.getStatusLine()); } } catch (URIException e) { - if(log.isDebugEnabled()) log.debug(e); + if (log.isDebugEnabled()) log.debug(e); } } } private class TwoWayCallable extends OneWayCallable { - public TwoWayCallable(HttpMethod method, String mexId, Operation operation) { - super(method, mexId, operation); + public TwoWayCallable(org.apache.commons.httpclient.HttpClient client, HttpMethod method, String mexId, Operation operation) { + super(client, method, mexId, operation); } public void processResponse(final int statusCode) { diff --git a/axis2/src/main/java/org/apache/ode/axis2/httpbinding/HttpMethodBuilder.java b/axis2/src/main/java/org/apache/ode/axis2/httpbinding/HttpMethodBuilder.java deleted file mode 100644 index 1118c13..0000000 --- a/axis2/src/main/java/org/apache/ode/axis2/httpbinding/HttpMethodBuilder.java +++ /dev/null @@ -1,208 +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.axis2.httpbinding; - -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.PostMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.commons.httpclient.methods.RequestEntity; -import org.apache.commons.httpclient.methods.StringRequestEntity; -import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; -import org.apache.commons.httpclient.methods.DeleteMethod; -import org.apache.commons.httpclient.methods.PutMethod; -import org.apache.commons.httpclient.methods.ExpectContinueMethod; -import org.apache.commons.httpclient.methods.EntityEnclosingMethod; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.ode.bpel.iapi.PartnerRoleMessageExchange; -import org.apache.ode.bpel.epr.MutableEndpoint; -import org.apache.ode.utils.DOMUtils; -import org.apache.ode.utils.Namespaces; -import org.apache.ode.utils.stl.CollectionsX; -import org.apache.ode.utils.wsdl.*; -import org.apache.ode.utils.wsdl.Messages; -import org.apache.ode.axis2.util.UrlReplacementTransformer; -import org.apache.ode.axis2.util.URLEncodedTransformer; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import javax.wsdl.Operation; -import javax.wsdl.BindingOperation; -import javax.wsdl.Message; -import javax.wsdl.Part; -import javax.wsdl.BindingInput; -import javax.wsdl.Binding; -import javax.wsdl.extensions.http.HTTPOperation; -import javax.wsdl.extensions.http.HTTPBinding; -import javax.wsdl.extensions.UnknownExtensibilityElement; -import javax.xml.namespace.QName; -import java.io.UnsupportedEncodingException; -import java.util.Map; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Collection; - -import com.ibm.wsdl.PartImpl; -import com.ibm.wsdl.util.StringUtils; - -/** - * @author Alexis Midon - */ -public class HttpMethodBuilder { - - private static final String CONTENT_TYPE_TEXT_XML = "text/xml"; - private static final Log log = LogFactory.getLog(HttpMethodBuilder.class); - - protected static final org.apache.ode.utils.wsdl.Messages msgs = Messages.getMessages(Messages.class); - protected Binding binding; - - public HttpMethodBuilder(Binding binding) { - this.binding = binding; - } - - - public HttpMethod buildHttpMethod(PartnerRoleMessageExchange odeMex) throws UnsupportedEncodingException { - Operation operation = odeMex.getOperation(); - BindingOperation bindingOperation = binding.getBindingOperation(operation.getName(), operation.getInput().getName(), operation.getOutput().getName()); - - // message to be sent - Element message = odeMex.getRequest().getMessage(); - Message msgDef = operation.getInput().getMessage(); - - // base url - String url = ((MutableEndpoint) odeMex.getEndpointReference()).getUrl(); - - // extract part values into a map and check that all parts are assigned a value - Map partElements = extractPartElements(msgDef, message); - - // http method type - // the operation may override the verb, this is an extension for RESTful BPEL - String verb = WsdlUtils.resolveVerb(binding, bindingOperation); - - // build the http method itself - HttpMethod method = prepareHttpMethod(bindingOperation, verb, partElements, url); - return method; - } - - protected Map extractPartElements(Message msgDef, Element message) { - Map partValues = new HashMap(); - for (Iterator iterator = msgDef.getParts().values().iterator(); iterator.hasNext();) { - Part part = (Part) iterator.next(); - Element partEl = DOMUtils.findChildByName(message, new QName(null, part.getName())); - if (partEl == null) - throw new IllegalArgumentException(msgs.msgOdeMessageMissingRequiredPart(part.getName())); - partValues.put(part.getName(), partEl); - } - return partValues; - } - - protected HttpMethod prepareHttpMethod(BindingOperation bindingOperation, String verb, Map partValues, final String rootUri) throws UnsupportedEncodingException { - if (log.isDebugEnabled()) log.debug("Preparing http request..."); - // convenience variables... - BindingInput bindingInput = bindingOperation.getBindingInput(); - HTTPOperation httpOperation = (HTTPOperation) WsdlUtils.getOperationExtension(bindingOperation); - String contentType = WsdlUtils.getMimeContentType(bindingInput.getExtensibilityElements()); - boolean useUrlEncoded = WsdlUtils.useUrlEncoded(bindingInput) || PostMethod.FORM_URL_ENCODED_CONTENT_TYPE.equalsIgnoreCase(contentType); - boolean useUrlReplacement = WsdlUtils.useUrlReplacement(bindingInput); - - final UrlReplacementTransformer replacementTransformer = new UrlReplacementTransformer(partValues.keySet()); - final URLEncodedTransformer encodedTransformer = new URLEncodedTransformer(); - - // the http method to be built and returned - HttpMethod method = null; - // the 4 elements the http method may be made of - String relativeUri = httpOperation.getLocationURI(); - String queryPath = null; - RequestEntity requestEntity = null; - String encodedParams = null; - - - if (useUrlReplacement) { - // insert part values in the url - relativeUri = replacementTransformer.transform(relativeUri, partValues); - } else if (useUrlEncoded) { - // encode part values - encodedParams = encodedTransformer.transform(partValues); - } - - // http-client api is not really neat - // something similar to the following would save some if/else manipulations. - // But we have to deal with it as-is. - // - // method = new Method(verb); - // method.setRequestEnity(..) - // etc... - if ("GET".equalsIgnoreCase(verb) || "DELETE".equalsIgnoreCase(verb)) { - if ("GET".equalsIgnoreCase(verb)) { - method = new GetMethod(); - } else if ("DELETE".equalsIgnoreCase(verb)) { - method = new DeleteMethod(); - } - - if (useUrlEncoded) { - queryPath = encodedParams; - } - - // Let http-client manage the redirection - // see org.apache.commons.httpclient.params.HttpClientParams.MAX_REDIRECTS - // default is 100 - method.setFollowRedirects(true); - } else if ("POST".equalsIgnoreCase(verb) || "PUT".equalsIgnoreCase(verb)) { - - if ("POST".equalsIgnoreCase(verb)) { - method = new PostMethod(); - } else if ("PUT".equalsIgnoreCase(verb)) { - method = new PutMethod(); - } - - // some body-building... - if (useUrlEncoded) { - requestEntity = new StringRequestEntity(encodedParams, PostMethod.FORM_URL_ENCODED_CONTENT_TYPE, "UTF-8"); - } else if (contentType.endsWith(CONTENT_TYPE_TEXT_XML)) { - // assumption is made that there is a single part - // validation steps in the constructor must warranty that - Part part = (Part) bindingOperation.getOperation().getInput().getMessage().getParts().values().iterator().next(); - Element partValue = partValues.get(part.getName()); - // if the part has an element name, we must take the first element - if (part.getElementName() != null) { - partValue = DOMUtils.getFirstChildElement(partValue); - } - String xmlString = DOMUtils.domToString(partValue); - requestEntity = new ByteArrayRequestEntity(xmlString.getBytes(), contentType); - } else { - // should not happen because of HttpBindingValidator, but never say never - throw new IllegalArgumentException("Unsupported content-type!"); - } - - // cast safely, PUT and POST are subclasses of EntityEnclosingMethod - ((EntityEnclosingMethod) method).setRequestEntity(requestEntity); - - } else { - // should not happen because of HttpBindingValidator, but never say never - throw new IllegalArgumentException("Unsupported HTTP method: " + verb); - } - - // Settings common to all methods - String completeUri = rootUri + (rootUri.endsWith("/") || relativeUri.startsWith("/") ? "" : "/") + relativeUri; - method.setPath(completeUri); // assumes that the path is properly encoded (URL safe). - method.setQueryString(queryPath); - return method; - } -} diff --git a/axis2/src/main/java/org/apache/ode/axis2/httpbinding/ProxyConf.java b/axis2/src/main/java/org/apache/ode/axis2/httpbinding/ProxyConf.java new file mode 100644 index 0000000..619ab6c --- /dev/null +++ b/axis2/src/main/java/org/apache/ode/axis2/httpbinding/ProxyConf.java @@ -0,0 +1,109 @@ +/* + * 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.axis2.httpbinding; + +import org.apache.axis2.transport.http.HttpTransportProperties; +import org.apache.commons.httpclient.Credentials; +import org.apache.commons.httpclient.HostConfiguration; +import org.apache.commons.httpclient.HttpState; +import org.apache.commons.httpclient.NTCredentials; +import org.apache.commons.httpclient.UsernamePasswordCredentials; +import org.apache.commons.httpclient.URIException; +import org.apache.commons.httpclient.auth.AuthScope; +import org.apache.commons.httpclient.params.HttpParams; +import org.apache.ode.axis2.Properties; + + +/** + * The purpose of this class is to configure proxy for HttpClient. + */ +public class ProxyConf { + + + // these properties are java system properties + // see http://java.sun.com/j2se/1.5.0/docs/guide/net/properties.html + public static final String HTTP_PROXY_HOST = "http.proxyHost"; + public static final String HTTP_PROXY_PORT = "http.proxyPort"; + public static final String HTTP_NON_PROXY_HOSTS = "http.nonProxyHosts"; + + public static void configure(HostConfiguration hostConfig, HttpState state, HttpTransportProperties.ProxyProperties proxyProperties) { + String proxyHost = proxyProperties.getProxyHostName(); + int proxyPort = proxyProperties.getProxyPort(); + + //Setting credentials + String userName = proxyProperties.getUserName(); + String password = proxyProperties.getPassWord(); + String domain = proxyProperties.getDomain(); + + Credentials proxyCred; + if (userName != null && password != null && domain != null) { + proxyCred = new NTCredentials(userName, password, proxyHost, domain); + } else if (userName != null) { + proxyCred = new UsernamePasswordCredentials(userName, password); + } else { + proxyCred = new UsernamePasswordCredentials("", ""); + } + + //Using Java Networking Properties + String host = System.getProperty(HTTP_PROXY_HOST); + if (host != null) { + proxyHost = host; + proxyCred = new UsernamePasswordCredentials("", ""); + } + String port = System.getProperty(HTTP_PROXY_PORT); + if (port != null) { + proxyPort = Integer.parseInt(port); + } + state.setProxyCredentials(AuthScope.ANY, proxyCred); + hostConfig.setProxy(proxyHost, proxyPort); + } + + /** + * @return true if a proxy is set in the params, or in the system property "http.proxyHost" + * and the host is not mentionnned in the system property "http.nonProxyHosts" + * @see Properties#PROP_HTTP_PROXY_PREFIX + */ + public static boolean isProxyEnabled(HttpParams params, String targetHost) throws URIException { + // from IL properties + boolean isSet = params.isParameterSet(Properties.PROP_HTTP_PROXY_PREFIX); + // from Java Networking Properties + isSet |= System.getProperty(HTTP_PROXY_HOST) != null; + + boolean isNonProxyHost = isNonProxyHost(targetHost); + return isSet && !isNonProxyHost; + } + + /** + * + * @return true if the targetHost is mentionned in the system properties "http.nonProxyHosts" + * @see http://java.sun.com/j2se/1.5.0/docs/guide/net/properties.html + */ + public static boolean isNonProxyHost(String targetHost) { + String nonProxyHosts = System.getProperty(HTTP_NON_PROXY_HOSTS); + if (nonProxyHosts != null) { + String[] splitted = nonProxyHosts.split("\\|"); + for (int i = 0; i < splitted.length; i++) { + if (targetHost.matches(splitted[i])) return true; + } + } + return false; + } +} + diff --git a/axis2/src/test/java/org/apache/ode/axis2/httpbinding/HttpClientHelperTest.java b/axis2/src/test/java/org/apache/ode/axis2/httpbinding/HttpClientHelperTest.java new file mode 100644 index 0000000..6595bc8 --- /dev/null +++ b/axis2/src/test/java/org/apache/ode/axis2/httpbinding/HttpClientHelperTest.java @@ -0,0 +1,351 @@ +/* + * 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.axis2.httpbinding; + +import junit.framework.TestCase; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.params.DefaultHttpParams; +import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.ode.bpel.epr.MutableEndpoint; +import org.apache.ode.bpel.iapi.BpelEngineException; +import org.apache.ode.bpel.iapi.EndpointReference; +import org.apache.ode.bpel.iapi.Message; +import org.apache.ode.bpel.iapi.PartnerRoleChannel; +import org.apache.ode.bpel.iapi.PartnerRoleMessageExchange; +import org.apache.ode.bpel.iapi.ProcessConf; +import org.apache.ode.utils.DOMUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import javax.wsdl.Binding; +import javax.wsdl.Definition; +import javax.wsdl.Operation; +import javax.wsdl.Port; +import javax.wsdl.PortType; +import javax.wsdl.Service; +import javax.wsdl.extensions.http.HTTPAddress; +import javax.wsdl.factory.WSDLFactory; +import javax.wsdl.xml.WSDLReader; +import javax.xml.namespace.QName; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Alexis Midon + */ +public class HttpClientHelperTest extends TestCase { + + protected Definition definition; + + protected HttpClientHelper deliciousBuilder; + protected Binding deliciousBinding; + protected Port deliciousPort; + + protected HttpClientHelper dummyBuilder; + protected Port dummyPort; + protected Binding dummyBinding; + + protected void setUp() throws Exception { + super.setUp(); + + URL wsdlURL = getClass().getResource("/http-method-builder.wsdl"); + WSDLReader wsdlReader = WSDLFactory.newInstance().newWSDLReader(); + wsdlReader.setFeature("javax.wsdl.verbose", false); + definition = wsdlReader.readWSDL(wsdlURL.toURI().toString()); + + Service deliciousService = definition.getService(new QName("http://ode/bpel/unit-test.wsdl", "DeliciousService")); + deliciousPort = deliciousService.getPort("TagHttpPort"); + deliciousBinding = deliciousPort.getBinding(); + deliciousBuilder = new HttpClientHelper(deliciousBinding); + + Service dummyService = definition.getService(new QName("http://ode/bpel/unit-test.wsdl", "DummyService")); + dummyPort = dummyService.getPort("DummyServiceHttpport"); + dummyBinding = dummyPort.getBinding(); + dummyBuilder = new HttpClientHelper(dummyBinding); + + } + + public void testGetTag() throws Exception { + String uri = ((HTTPAddress) deliciousPort.getExtensibilityElements().get(0)).getLocationURI(); + String expectedUri = uri + "/tag/java"; + Element msgEl; + { + Document odeMsg = DOMUtils.newDocument(); + msgEl = odeMsg.createElementNS(null, "message"); + Element partEl = odeMsg.createElementNS(null, "TagPart"); + partEl.setTextContent("java"); + odeMsg.appendChild(msgEl); + msgEl.appendChild(partEl); + } + + MockMessageExchange odeMex = new MockMessageExchange(); + odeMex.op = deliciousBinding.getBindingOperation("getTag", null, null).getOperation(); + odeMex.req = new MockMessage(msgEl); + odeMex.epr = new MockEPR(uri); + HttpMethod httpMethod = deliciousBuilder.buildHttpMethod(odeMex, new DefaultHttpParams()); + + + assertTrue("GET".equalsIgnoreCase(httpMethod.getName())); + assertTrue(expectedUri.equalsIgnoreCase(httpMethod.getURI().toString())); + } + + public void testGetTagWithNoPart() throws Exception { + String uri = ((HTTPAddress) deliciousPort.getExtensibilityElements().get(0)).getLocationURI(); + Element msgEl; + { + Document odeMsg = DOMUtils.newDocument(); + msgEl = odeMsg.createElementNS(null, "message"); + odeMsg.appendChild(msgEl); + } + + MockMessageExchange odeMex = new MockMessageExchange(); + odeMex.op = deliciousBinding.getBindingOperation("getTag", null, null).getOperation(); + odeMex.req = new MockMessage(msgEl); + odeMex.epr = new MockEPR(uri); + try { + HttpMethod httpMethod = deliciousBuilder.buildHttpMethod(odeMex, new DefaultHttpParams()); + fail("IllegalArgumentException expected because message element is empty."); + } catch (IllegalArgumentException e) { + // expected behavior + } + } + + public void testHello() throws Exception { + String uri = ((HTTPAddress) dummyPort.getExtensibilityElements().get(0)).getLocationURI(); + String expectedUri = uri + "/" + "DummyService/hello"; + Element msgEl, helloEl; + { + Document odeMsg = DOMUtils.newDocument(); + msgEl = odeMsg.createElementNS(null, "message"); + Element partEl = odeMsg.createElementNS(null, "parameters"); + odeMsg.appendChild(msgEl); + msgEl.appendChild(partEl); + helloEl = odeMsg.createElementNS(null, "hello"); + helloEl.setTextContent("This is a test. How is it going so far?"); + partEl.appendChild(helloEl); + } + + MockMessageExchange odeMex = new MockMessageExchange(); + odeMex.op = dummyBinding.getBindingOperation("hello", null, null).getOperation(); + odeMex.req = new MockMessage(msgEl); + odeMex.epr = new MockEPR(uri); + HttpMethod httpMethod = dummyBuilder.buildHttpMethod(odeMex, new DefaultHttpParams()); + assertTrue("POST".equalsIgnoreCase(httpMethod.getName())); + assertEquals("Generated URI does not match", expectedUri, httpMethod.getURI().toString()); + + byte[] content = ((ByteArrayRequestEntity) ((PostMethod) httpMethod).getRequestEntity()).getContent(); + String b = new String(content); + assertEquals("Invalid body in generated http query", DOMUtils.domToString(helloEl), b); + } + + + class MockEPR implements EndpointReference, MutableEndpoint { + String url; + + MockEPR(String url) { + this.url = url; + } + + public String getUrl() { + return url; + } + + // other useless methods + public Document toXML() { + return null; + } + + public boolean accept(Node node) { + return false; + } + + public void fromMap(Map eprMap) { + + } + + public void set(Node node) { + + } + + public Map toMap() { + return null; + } + } + + class MockMessage implements Message { + Element elt; + + MockMessage(Element elt) { + this.elt = elt; + } + + public Element getMessage() { + return elt; + } + + // other useless methods + public Element getHeaderPart(String partName) { + return null; + } + + public Map getHeaderParts() { + return null; + } + + public Element getPart(String partName) { + return null; + } + + public List getParts() { + return null; + } + + public QName getType() { + return null; + } + + public void setHeaderPart(String name, Element content) { + + } + + public void setMessage(Element msg) { + + } + + public void setPart(String partName, Element content) { + + } + } + + class MockMessageExchange implements PartnerRoleMessageExchange { + Operation op; + Message req; + EndpointReference epr; + + public Operation getOperation() { + return op; + } + + public Message getRequest() { + return req; + } + + public EndpointReference getEndpointReference() throws BpelEngineException { + return epr; + } + + // other useless methods + public QName getCaller() { + return null; + } + + public PartnerRoleChannel getChannel() { + return null; + } + + public EndpointReference getMyRoleEndpointReference() { + return null; + } + + public void reply(Message response) throws BpelEngineException { + + } + + public void replyAsync() { + + } + + public void replyOneWayOk() { + + } + + public void replyWithFailure(FailureType type, String description, Element details) throws BpelEngineException { + + } + + public void replyWithFault(QName faultType, Message outputFaultMessage) throws BpelEngineException { + + } + + public Message createMessage(QName msgType) { + return null; + } + + public QName getFault() { + return null; + } + + public String getFaultExplanation() { + return null; + } + + public Message getFaultResponse() { + return null; + } + + public String getMessageExchangeId() throws BpelEngineException { + return null; + } + + public MessageExchangePattern getMessageExchangePattern() { + return null; + } + + public String getOperationName() throws BpelEngineException { + return null; + } + + public PortType getPortType() { + return null; + } + + public String getProperty(String key) { + return null; + } + + public Set getPropertyNames() { + return null; + } + + public Message getResponse() { + return null; + } + + public Status getStatus() { + return null; + } + + public boolean isTransactionPropagated() throws BpelEngineException { + return false; + } + + public void release() { + + } + + public void setProperty(String key, String value) { + + } + } +} diff --git a/axis2/src/test/java/org/apache/ode/axis2/httpbinding/HttpMethodBuilderTest.java b/axis2/src/test/java/org/apache/ode/axis2/httpbinding/HttpMethodBuilderTest.java deleted file mode 100644 index cd5b939..0000000 --- a/axis2/src/test/java/org/apache/ode/axis2/httpbinding/HttpMethodBuilderTest.java +++ /dev/null @@ -1,349 +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.axis2.httpbinding; - -import junit.framework.TestCase; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; -import org.apache.commons.httpclient.methods.PostMethod; -import org.apache.ode.bpel.epr.MutableEndpoint; -import org.apache.ode.bpel.iapi.BpelEngineException; -import org.apache.ode.bpel.iapi.EndpointReference; -import org.apache.ode.bpel.iapi.Message; -import org.apache.ode.bpel.iapi.PartnerRoleChannel; -import org.apache.ode.bpel.iapi.PartnerRoleMessageExchange; -import org.apache.ode.utils.DOMUtils; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import javax.wsdl.Binding; -import javax.wsdl.Definition; -import javax.wsdl.Operation; -import javax.wsdl.Port; -import javax.wsdl.PortType; -import javax.wsdl.Service; -import javax.wsdl.extensions.http.HTTPAddress; -import javax.wsdl.factory.WSDLFactory; -import javax.wsdl.xml.WSDLReader; -import javax.xml.namespace.QName; -import java.net.URL; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * @author Alexis Midon - */ -public class HttpMethodBuilderTest extends TestCase { - - protected Definition definition; - - protected HttpMethodBuilder deliciousBuilder; - protected Binding deliciousBinding; - protected Port deliciousPort; - - protected HttpMethodBuilder dummyBuilder; - protected Port dummyPort; - protected Binding dummyBinding; - - protected void setUp() throws Exception { - super.setUp(); - - URL wsdlURL = getClass().getResource("/http-method-builder.wsdl"); - WSDLReader wsdlReader = WSDLFactory.newInstance().newWSDLReader(); - wsdlReader.setFeature("javax.wsdl.verbose", false); - definition = wsdlReader.readWSDL(wsdlURL.toURI().toString()); - - Service deliciousService = definition.getService(new QName("http://ode/bpel/unit-test.wsdl", "DeliciousService")); - deliciousPort = deliciousService.getPort("TagHttpPort"); - deliciousBinding = deliciousPort.getBinding(); - deliciousBuilder = new HttpMethodBuilder(deliciousBinding); - - Service dummyService = definition.getService(new QName("http://ode/bpel/unit-test.wsdl", "DummyService")); - dummyPort = dummyService.getPort("DummyServiceHttpport"); - dummyBinding = dummyPort.getBinding(); - dummyBuilder = new HttpMethodBuilder(dummyBinding); - - } - - public void testGetTag() throws Exception { - String uri = ((HTTPAddress) deliciousPort.getExtensibilityElements().get(0)).getLocationURI(); - String expectedUri = uri + "/tag/java"; - Element msgEl; - { - Document odeMsg = DOMUtils.newDocument(); - msgEl = odeMsg.createElementNS(null, "message"); - Element partEl = odeMsg.createElementNS(null, "TagPart"); - partEl.setTextContent("java"); - odeMsg.appendChild(msgEl); - msgEl.appendChild(partEl); - } - - MockMessageExchange odeMex = new MockMessageExchange(); - odeMex.op = deliciousBinding.getBindingOperation("getTag", null, null).getOperation(); - odeMex.req = new MockMessage(msgEl); - odeMex.epr = new MockEPR(uri); - HttpMethod httpMethod = deliciousBuilder.buildHttpMethod(odeMex); - - - assertTrue("GET".equalsIgnoreCase(httpMethod.getName())); - assertTrue(expectedUri.equalsIgnoreCase(httpMethod.getURI().toString())); - } - - public void testGetTagWithNoPart() throws Exception { - String uri = ((HTTPAddress) deliciousPort.getExtensibilityElements().get(0)).getLocationURI(); - Element msgEl; - { - Document odeMsg = DOMUtils.newDocument(); - msgEl = odeMsg.createElementNS(null, "message"); - odeMsg.appendChild(msgEl); - } - - MockMessageExchange odeMex = new MockMessageExchange(); - odeMex.op = deliciousBinding.getBindingOperation("getTag", null, null).getOperation(); - odeMex.req = new MockMessage(msgEl); - odeMex.epr = new MockEPR(uri); - try { - HttpMethod httpMethod = deliciousBuilder.buildHttpMethod(odeMex); - fail("IllegalArgumentException expected because message element is empty."); - } catch (IllegalArgumentException e) { - // expected behavior - } - } - - public void testHello() throws Exception { - String uri = ((HTTPAddress) dummyPort.getExtensibilityElements().get(0)).getLocationURI(); - String expectedUri = uri + "/" + "DummyService/hello"; - Element msgEl, helloEl; - { - Document odeMsg = DOMUtils.newDocument(); - msgEl = odeMsg.createElementNS(null, "message"); - Element partEl = odeMsg.createElementNS(null, "parameters"); - odeMsg.appendChild(msgEl); - msgEl.appendChild(partEl); - helloEl = odeMsg.createElementNS(null, "hello"); - helloEl.setTextContent("This is a test. How is it going so far?"); - partEl.appendChild(helloEl); - } - - MockMessageExchange odeMex = new MockMessageExchange(); - odeMex.op = dummyBinding.getBindingOperation("hello", null, null).getOperation(); - odeMex.req = new MockMessage(msgEl); - odeMex.epr = new MockEPR(uri); - HttpMethod httpMethod = dummyBuilder.buildHttpMethod(odeMex); - assertTrue("POST".equalsIgnoreCase(httpMethod.getName())); - assertEquals("Generated URI does not match", expectedUri, httpMethod.getURI().toString()); - - byte[] content = ((ByteArrayRequestEntity) ((PostMethod) httpMethod).getRequestEntity()).getContent(); - String b = new String(content); - assertEquals("Invalid body in generated http query", DOMUtils.domToString(helloEl), b); - } - - - class MockEPR implements EndpointReference, MutableEndpoint { - String url; - - MockEPR(String url) { - this.url = url; - } - - public String getUrl() { - return url; - } - - // other useless methods - public Document toXML() { - return null; - } - - public boolean accept(Node node) { - return false; - } - - public void fromMap(Map eprMap) { - - } - - public void set(Node node) { - - } - - public Map toMap() { - return null; - } - } - - class MockMessage implements Message { - Element elt; - - MockMessage(Element elt) { - this.elt = elt; - } - - public Element getMessage() { - return elt; - } - - // other useless methods - public Element getHeaderPart(String partName) { - return null; - } - - public Map getHeaderParts() { - return null; - } - - public Element getPart(String partName) { - return null; - } - - public List getParts() { - return null; - } - - public QName getType() { - return null; - } - - public void setHeaderPart(String name, Element content) { - - } - - public void setMessage(Element msg) { - - } - - public void setPart(String partName, Element content) { - - } - } - - class MockMessageExchange implements PartnerRoleMessageExchange { - Operation op; - Message req; - EndpointReference epr; - - public Operation getOperation() { - return op; - } - - public Message getRequest() { - return req; - } - - public EndpointReference getEndpointReference() throws BpelEngineException { - return epr; - } - - // other useless methods - public QName getCaller() { - return null; - } - - public PartnerRoleChannel getChannel() { - return null; - } - - public EndpointReference getMyRoleEndpointReference() { - return null; - } - - public void reply(Message response) throws BpelEngineException { - - } - - public void replyAsync() { - - } - - public void replyOneWayOk() { - - } - - public void replyWithFailure(FailureType type, String description, Element details) throws BpelEngineException { - - } - - public void replyWithFault(QName faultType, Message outputFaultMessage) throws BpelEngineException { - - } - - public Message createMessage(QName msgType) { - return null; - } - - public QName getFault() { - return null; - } - - public String getFaultExplanation() { - return null; - } - - public Message getFaultResponse() { - return null; - } - - public String getMessageExchangeId() throws BpelEngineException { - return null; - } - - public MessageExchangePattern getMessageExchangePattern() { - return null; - } - - public String getOperationName() throws BpelEngineException { - return null; - } - - public PortType getPortType() { - return null; - } - - public String getProperty(String key) { - return null; - } - - public Set getPropertyNames() { - return null; - } - - public Message getResponse() { - return null; - } - - public Status getStatus() { - return null; - } - - public boolean isTransactionPropagated() throws BpelEngineException { - return false; - } - - public void release() { - - } - - public void setProperty(String key, String value) { - - } - } -} diff --git a/bpel-api/src/main/java/org/apache/ode/bpel/common/CorrelationKey.java b/bpel-api/src/main/java/org/apache/ode/bpel/common/CorrelationKey.java index 8114bc7..62be326 100644 --- a/bpel-api/src/main/java/org/apache/ode/bpel/common/CorrelationKey.java +++ b/bpel-api/src/main/java/org/apache/ode/bpel/common/CorrelationKey.java @@ -23,7 +23,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import org.apache.ode.utils.ArrayUtils; +import org.apache.ode.utils.CollectionUtils; /** *

@@ -173,7 +173,7 @@ public class CorrelationKey implements Serializable { buf.append("setId="); buf.append(_csetId); buf.append(", values="); - buf.append(ArrayUtils.makeCollection(ArrayList.class, _keyValues)); + buf.append(CollectionUtils.makeCollection(ArrayList.class, _keyValues)); buf.append('}'); return buf.toString(); diff --git a/bpel-api/src/main/java/org/apache/ode/bpel/common/Filter.java b/bpel-api/src/main/java/org/apache/ode/bpel/common/Filter.java index 2916fcd..9f87ace 100644 --- a/bpel-api/src/main/java/org/apache/ode/bpel/common/Filter.java +++ b/bpel-api/src/main/java/org/apache/ode/bpel/common/Filter.java @@ -19,7 +19,7 @@ package org.apache.ode.bpel.common; -import org.apache.ode.utils.ArrayUtils; +import org.apache.ode.utils.CollectionUtils; import org.apache.ode.utils.msg.MessageBundle; import org.apache.ode.utils.stl.CollectionsX; import org.apache.ode.utils.stl.UnaryFunction; @@ -133,7 +133,7 @@ public abstract class Filter implements Serializable { private Collection getFilterKeysStr() { return CollectionsX.transform(new ArrayList(), - ArrayUtils.makeCollection(ArrayList.class , getFilterKeys()), + CollectionUtils.makeCollection(ArrayList.class , getFilterKeys()), new UnaryFunction() { public String apply(FKEY x) { return x.name(); diff --git a/bpel-api/src/main/java/org/apache/ode/bpel/evt/BpelEvent.java b/bpel-api/src/main/java/org/apache/ode/bpel/evt/BpelEvent.java index 1ce14a5..925e3d8 100644 --- a/bpel-api/src/main/java/org/apache/ode/bpel/evt/BpelEvent.java +++ b/bpel-api/src/main/java/org/apache/ode/bpel/evt/BpelEvent.java @@ -18,7 +18,7 @@ */ package org.apache.ode.bpel.evt; -import org.apache.ode.utils.ArrayUtils; +import org.apache.ode.utils.CollectionUtils; import java.io.Serializable; import java.lang.reflect.Method; @@ -66,7 +66,7 @@ public abstract class BpelEvent implements Serializable { if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) { try { String field = method.getName().substring(3); - Object value = method.invoke(this, ArrayUtils.EMPTY_OBJECT_ARRAY); + Object value = method.invoke(this, CollectionUtils.EMPTY_OBJECT_ARRAY); if (value == null) { continue; } 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 81525e0..1a18def 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 @@ -24,6 +24,7 @@ import java.net.URI; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Properties; import javax.wsdl.Definition; import javax.xml.namespace.QName; @@ -110,7 +111,7 @@ public interface ProcessConf { * Get the deployment properties. * @return */ - Map getProperties(); + Map getDeploymentProperties(); /** * Gets the name of the package into which the process is deployed. @@ -155,5 +156,11 @@ public interface ProcessConf { List getExtensionElement(QName qname); boolean isEventEnabled(List scopeNames, BpelEvent.TYPE type); - + + /** + * Must not return null values. + * @param path + * @return + */ + public Map getProperties(String... path); } diff --git a/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/DebuggerSupport.java b/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/DebuggerSupport.java index 703bfe1..e74f07f 100644 --- a/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/DebuggerSupport.java +++ b/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/DebuggerSupport.java @@ -37,7 +37,7 @@ import org.apache.ode.bpel.pmapi.InstanceNotFoundException; import org.apache.ode.bpel.pmapi.ManagementException; import org.apache.ode.bpel.pmapi.ProcessingException; import org.apache.ode.bpel.runtime.breaks.BreakpointImpl; -import org.apache.ode.utils.ArrayUtils; +import org.apache.ode.utils.CollectionUtils; import org.apache.ode.utils.msg.MessageBundle; import javax.xml.namespace.QName; @@ -102,7 +102,7 @@ class DebuggerSupport { } void addGlobalBreakpoint(Breakpoint breakpoint){ - Collection c = ArrayUtils.makeCollection(ArrayList.class, _globalBreakPoints); + Collection c = CollectionUtils.makeCollection(ArrayList.class, _globalBreakPoints); c.add(breakpoint); _globalBreakPoints = c.toArray(new Breakpoint[c.size()]); } @@ -113,7 +113,7 @@ class DebuggerSupport { bpArr = new Breakpoint[]{breakpoint}; } else{ - Collection c = ArrayUtils.makeCollection(ArrayList.class, bpArr); + Collection c = CollectionUtils.makeCollection(ArrayList.class, bpArr); c.add(breakpoint); bpArr = c.toArray(new Breakpoint[c.size()]); } @@ -121,7 +121,7 @@ class DebuggerSupport { } void removeGlobalBreakpoint(Breakpoint breakpoint){ - Collection c = ArrayUtils.makeCollection(ArrayList.class, _globalBreakPoints); + Collection c = CollectionUtils.makeCollection(ArrayList.class, _globalBreakPoints); c.remove(breakpoint); _globalBreakPoints = c.toArray(new Breakpoint[c.size()]); } @@ -129,7 +129,7 @@ class DebuggerSupport { void removeBreakpoint(Long pid, Breakpoint breakpoint){ Breakpoint[] bpArr = _instanceBreakPoints.get(pid); if(bpArr != null){ - Collection c = ArrayUtils.makeCollection(ArrayList.class, bpArr); + Collection c = CollectionUtils.makeCollection(ArrayList.class, bpArr); c.remove(breakpoint); bpArr = c.toArray(new Breakpoint[c.size()]); if(bpArr.length == 0) { diff --git a/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/MyRoleMessageExchangeImpl.java b/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/MyRoleMessageExchangeImpl.java index 9cea12e..0490abe 100644 --- a/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/MyRoleMessageExchangeImpl.java +++ b/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/MyRoleMessageExchangeImpl.java @@ -42,8 +42,8 @@ import java.util.concurrent.TimeoutException; class MyRoleMessageExchangeImpl extends MessageExchangeImpl implements MyRoleMessageExchange { + private static final Log __log = LogFactory.getLog(MyRoleMessageExchangeImpl.class); - public static final int TIMEOUT = 2 * 60 * 1000; private static Map _waitingCallbacks = new ConcurrentHashMap(); diff --git a/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/PartnerLinkMyRoleImpl.java b/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/PartnerLinkMyRoleImpl.java index 9da1cfd..93dd20a 100644 --- a/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/PartnerLinkMyRoleImpl.java +++ b/bpel-runtime/src/main/java/org/apache/ode/bpel/engine/PartnerLinkMyRoleImpl.java @@ -41,7 +41,7 @@ import org.apache.ode.bpel.o.OProcess; import org.apache.ode.bpel.o.OScope; import org.apache.ode.bpel.runtime.InvalidProcessException; import org.apache.ode.bpel.runtime.PROCESS; -import org.apache.ode.utils.ArrayUtils; +import org.apache.ode.utils.CollectionUtils; import org.apache.ode.utils.ObjectPrinter; import org.apache.ode.utils.msg.MessageBundle; import org.w3c.dom.Element; @@ -125,7 +125,7 @@ class PartnerLinkMyRoleImpl extends PartnerLinkRoleImpl { String partnerSessionId = mex.getProperty(MessageExchange.PROPERTY_SEP_PARTNERROLE_SESSIONID); if (__log.isDebugEnabled()) { __log.debug("INPUTMSG: " + correlatorId + ": MSG RCVD keys=" - + ArrayUtils.makeCollection(HashSet.class, keys) + " mySessionId=" + mySessionId + + CollectionUtils.makeCollection(HashSet.class, keys) + " mySessionId=" + mySessionId + " partnerSessionId=" + partnerSessionId); } 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 22b5d2a..5e89ed3 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 @@ -739,7 +739,7 @@ public class ProcessAndInstanceManagementImpl implements InstanceManagement, Pro TProcessProperties properties = info.addNewProperties(); if (custom.includeProcessProperties()) { - for (Map.Entry propEntry : pconf.getProperties().entrySet()) { + for (Map.Entry propEntry : pconf.getDeploymentProperties().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 2c5f200..b4288fe 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().getProperties(); + Map props = ic.getProcessConf().getDeploymentProperties(); for (Map.Entry prop : props.entrySet()) { if (prop.getKey().equals(propertyName)) return ((Text)prop.getValue()).getWholeText(); diff --git a/bpel-runtime/src/main/java/org/apache/ode/bpel/memdao/CorrelatorDaoImpl.java b/bpel-runtime/src/main/java/org/apache/ode/bpel/memdao/CorrelatorDaoImpl.java index d71b908..7e3542e 100644 --- a/bpel-runtime/src/main/java/org/apache/ode/bpel/memdao/CorrelatorDaoImpl.java +++ b/bpel-runtime/src/main/java/org/apache/ode/bpel/memdao/CorrelatorDaoImpl.java @@ -25,7 +25,7 @@ import org.apache.ode.bpel.dao.CorrelatorDAO; import org.apache.ode.bpel.dao.MessageExchangeDAO; import org.apache.ode.bpel.dao.MessageRouteDAO; import org.apache.ode.bpel.dao.ProcessInstanceDAO; -import org.apache.ode.utils.ArrayUtils; +import org.apache.ode.utils.CollectionUtils; import java.util.ArrayList; import java.util.HashSet; @@ -58,7 +58,7 @@ class CorrelatorDaoImpl extends DaoBaseImpl implements CorrelatorDAO { } for (Iterator i = _messages.iterator(); i.hasNext();) { MsgQueueEntry mqe = (MsgQueueEntry)i.next(); - Set keyset = (Set)ArrayUtils.makeCollection(HashSet.class, mqe.keys); + Set keyset = (Set) CollectionUtils.makeCollection(HashSet.class, mqe.keys); if ((key == null) || keyset.contains(key)) { i.remove(); return mqe.message; @@ -93,7 +93,7 @@ class CorrelatorDaoImpl extends DaoBaseImpl implements CorrelatorDAO { public void enqueueMessage(MessageExchangeDAO mex, CorrelationKey[] keys) { if (__log.isDebugEnabled()) { __log.debug("enqueueProcessInvocation: data=" + mex + " keys=" - + ArrayUtils.makeCollection(ArrayList.class, keys)); + + CollectionUtils.makeCollection(ArrayList.class, keys)); } MsgQueueEntry mqe = new MsgQueueEntry(mex, keys); 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 eb89f60..57e2cf2 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,6 +59,8 @@ import java.util.Set; */ class DeploymentUnitDir { + static final String IL_PROPERTIES_FILE = "integration-layer.properties"; + private static Log __log = LogFactory.getLog(DeploymentUnitDir.class); private String _name; @@ -200,6 +202,10 @@ class DeploymentUnitDir { return _duDirectory; } + public File getILPropertyFile(){ + return new File(getDeployDir(), IL_PROPERTIES_FILE); + } + public DeployDocument getDeploymentDescriptor() { if (_dd == null) { File ddLocation = new File(_duDirectory, "deploy.xml"); 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 7e68188..124b561 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 @@ -32,6 +32,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.wsdl.Definition; import javax.xml.namespace.QName; @@ -52,6 +54,8 @@ import org.apache.ode.bpel.iapi.ProcessConf; import org.apache.ode.bpel.iapi.ProcessState; import org.apache.ode.store.DeploymentUnitDir.CBPInfo; import org.apache.ode.utils.DOMUtils; +import org.apache.ode.utils.HierarchiedProperties; +import org.apache.ode.utils.fs.FileWatchDog; import org.apache.ode.utils.msg.MessageBundle; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -69,7 +73,8 @@ public class ProcessConfImpl implements ProcessConf { private final Date _deployDate; private final Map _props; - private final HashMap _partnerRoleInitialValues = new HashMap();; + private final HashMap _partnerRoleInitialValues = new HashMap(); + private final HashMap _myRoleEndpoints = new HashMap(); private final Map> _events = new HashMap>(); private final ArrayList _mexi = new ArrayList(); @@ -83,6 +88,13 @@ public class ProcessConfImpl implements ProcessConf { // cache the inMemory flag because XMLBeans objects are heavily synchronized (guarded by a coarse-grained lock) private volatile boolean _inMemory = false; + // provide the IL properties + private HierarchiedProperties _ilProperties; + // monitor the IL property file and reload it if necessary + private ILWatchDog _ilWatchDog; + private final ReadWriteLock ilPropertiesLock = new ReentrantReadWriteLock(); + + ProcessConfImpl(QName pid, QName type, long version, DeploymentUnitDir du, TDeployment.Process pinfo, Date deployDate, Map props, ProcessState pstate) { _pid = pid; @@ -94,7 +106,8 @@ public class ProcessConfImpl implements ProcessConf { _state = pstate; _type = type; _inMemory = _pinfo.isSetInMemory() && _pinfo.getInMemory(); - + _ilWatchDog = new ILWatchDog(); + initLinks(); initMexInterceptors(); initEventList(); @@ -163,7 +176,7 @@ public class ProcessConfImpl implements ProcessConf { return _du.getName(); } - public Map getProperties() { + public Map getDeploymentProperties() { return _props; } @@ -188,7 +201,8 @@ public class ProcessConfImpl implements ProcessConf { throw new ContextException("CBP record not found for type " + getType()); try { String relative = getRelativePath(_du.getDeployDir(), cbpInfo.cbp).replaceAll("\\\\", "/"); - if (!relative.endsWith(".cbp")) throw new ContextException("CBP file must end with .cbp suffix: " + cbpInfo.cbp); + if (!relative.endsWith(".cbp")) + throw new ContextException("CBP file must end with .cbp suffix: " + cbpInfo.cbp); relative = relative.replace(".cbp", ".bpel"); File bpelFile = new File(_du.getDeployDir(), relative); if (!bpelFile.exists()) __log.warn("BPEL file does not exist: " + bpelFile); @@ -197,9 +211,9 @@ public class ProcessConfImpl implements ProcessConf { throw new ContextException("IOException in getBpelRelativePath: " + cbpInfo.cbp, e); } } - + public URI getBaseURI() { - return _du.getDeployDir().toURI(); + return _du.getDeployDir().toURI(); } public ProcessState getState() { @@ -272,6 +286,7 @@ public class ProcessConfImpl implements ProcessConf { public boolean isTransient() { return _inMemory; } + public void setTransient(boolean t) { _pinfo.setInMemory(t); _inMemory = t; @@ -279,13 +294,13 @@ public class ProcessConfImpl implements ProcessConf { public boolean isEventEnabled(List scopeNames, BpelEvent.TYPE type) { if (scopeNames != null) { - for (String scopeName : scopeNames) { - Set evtSet = _events.get(scopeName); - if (evtSet != null) { - if (evtSet.contains(type)) return true; + for (String scopeName : scopeNames) { + Set evtSet = _events.get(scopeName); + if (evtSet != null) { + if (evtSet.contains(type)) return true; + } } } - } Set evtSet = _events.get(null); if (evtSet != null) { // Default filtering at the process level for some event types @@ -302,7 +317,7 @@ public class ProcessConfImpl implements ProcessConf { for (BpelEvent.TYPE t : BpelEvent.TYPE.values()) { if (!t.equals(BpelEvent.TYPE.scopeHandling)) all.add(t); } - _events.put(null,all); + _events.put(null, all); return; } @@ -311,7 +326,7 @@ public class ProcessConfImpl implements ProcessConf { HashSet all = new HashSet(); for (BpelEvent.TYPE t : BpelEvent.TYPE.values()) all.add(t); - _events.put(null,all); + _events.put(null, all); return; } @@ -340,7 +355,8 @@ public class ProcessConfImpl implements ProcessConf { private String getRelativePath(File base, File path) throws IOException { String basePath = base.getCanonicalPath(); String cbpPath = path.getCanonicalPath(); - if (!cbpPath.startsWith(basePath)) throw new IOException("Invalid relative path: base="+base+" path="+path); + if (!cbpPath.startsWith(basePath)) + throw new IOException("Invalid relative path: base=" + base + " path=" + path); String relative = cbpPath.substring(basePath.length()); if (relative.startsWith(File.separator)) relative = relative.substring(1); return relative; @@ -354,4 +370,77 @@ 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!"); + + // update properties if necessary + // do it manually to save resources (instead of using a thread) + _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); + } finally { + ilPropertiesLock.readLock().unlock(); + } + } + } + + /** + * Manage the reloading of the propery file every {@link org.apache.ode.utils.fs.FileWatchDog#DEFAULT_DELAY}. + * The check is done manually, meaning that {@link #check()} must be invoked each time _ilProperties is accessed. + */ + private class ILWatchDog extends FileWatchDog { + public ILWatchDog() { + super(_du.getILPropertyFile()); + } + + protected void init() { + ilPropertiesLock.writeLock().lock(); + try { + if (_ilProperties == null) { + try { + _ilProperties = new HierarchiedProperties(super.file); + } catch (IOException e) { + throw new ContextException("Integration-Layer Properties cannot be loaded!", e); + } + } else { + _ilProperties.clear(); + } + } finally { + ilPropertiesLock.writeLock().unlock(); + } + } + + protected boolean isInitialized() { + return _ilProperties != null; + } + + protected void doOnUpdate() { + ilPropertiesLock.writeLock().lock(); + try { + init(); + try { + _ilProperties.loadFile(); + } catch (IOException e) { + throw new ContextException("Integration-Layer Properties cannot be loaded!", e); + } + } finally { + ilPropertiesLock.writeLock().unlock(); + } + } + } + + } 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 4a37722..a8b2c6e 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 @@ -256,7 +256,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.getProperties().entrySet()) { + for (Map.Entry prop : pc.getDeploymentProperties().entrySet()) { newDao.setProperty(prop.getKey(), DOMUtils.domToString(prop.getValue())); } deployed.add(pc.getProcessId()); diff --git a/bpel-store/src/test/resources/integration-layer.properties b/bpel-store/src/test/resources/integration-layer.properties new file mode 100644 index 0000000..488d8d5 --- /dev/null +++ b/bpel-store/src/test/resources/integration-layer.properties @@ -0,0 +1,55 @@ +# All properties follow this pattern: +# [servicename[.portname].ode.]property +# +# If service name is mentioned but port name omitted, the value will apply to all ports of the service. +# If service name and port name are omitted, the value will apply to all services. + +# For instance: +# Service-specific values +# my-soap-service1.ode.http.connection.timeout=50000 +# my-http-service1.ode.http.protocol.max-redirects=40 +# +# Port-specific values +# my-http-service1.portA.ode.http.protocol.max-redirects=60 + + +# 3 minutes +mex.timeout=180000 + +# true/false +http.request.chunk=true + +# HTTP/1.1 / HTTP/1.0 +http.protocol.version=HTTP/1.1 + +# true/false +# will not apply to http-bound services TBD +http.request.gzip=false + +# true/false +# Append gzip to the Accept-Encoding header +http.accept.gzip=false + +http.protocol.encoding=UTF-8 + + +# You must define one property per header, prefixed with 'http.default-headers' +# These values will override any values already set for a given header. +http.default-headers.User-Agent=your-agent +http.default-headers.foo=bar + +# timeout in milliseconds until a connection is etablished +http.connection.timeout=40000 +# timeout in milliseconds for waiting for data +http.socket.timeout=40000 + +http.protocol.max-redirects=10 + +# To disable proxy set the host to null +http.proxy.host=myproxy.org +http.proxy.port=8080 +http.proxy.domain=mydomain +http.proxy.user=johndoe +http.proxy.password=topsecret + + diff --git a/dao-hibernate/src/main/java/org/apache/ode/daohib/bpel/CorrelatorDaoImpl.java b/dao-hibernate/src/main/java/org/apache/ode/daohib/bpel/CorrelatorDaoImpl.java index 7713d3b..bd0e3d8 100644 --- a/dao-hibernate/src/main/java/org/apache/ode/daohib/bpel/CorrelatorDaoImpl.java +++ b/dao-hibernate/src/main/java/org/apache/ode/daohib/bpel/CorrelatorDaoImpl.java @@ -25,7 +25,6 @@ import java.util.Iterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.bpel.common.CorrelationKey; -import org.apache.ode.bpel.common.ProcessState; import org.apache.ode.bpel.dao.CorrelatorDAO; import org.apache.ode.bpel.dao.MessageExchangeDAO; import org.apache.ode.bpel.dao.MessageRouteDAO; @@ -36,7 +35,7 @@ import org.apache.ode.daohib.bpel.hobj.HCorrelatorMessage; import org.apache.ode.daohib.bpel.hobj.HCorrelatorSelector; import org.apache.ode.daohib.bpel.hobj.HMessageExchange; import org.apache.ode.daohib.bpel.hobj.HProcessInstance; -import org.apache.ode.utils.ArrayUtils; +import org.apache.ode.utils.CollectionUtils; import org.hibernate.Hibernate; import org.hibernate.LockMode; import org.hibernate.Query; @@ -147,7 +146,7 @@ class CorrelatorDaoImpl extends HibernateDao implements CorrelatorDAO { public void enqueueMessage(MessageExchangeDAO mex, CorrelationKey[] correlationKeys) { String[] keys = canonifyKeys(correlationKeys); String hdr = "enqueueMessage(mex=" + ((MessageExchangeDaoImpl) mex)._hobj.getId() + " keys=" - + ArrayUtils.makeCollection(ArrayList.class, keys) + "): "; + + CollectionUtils.makeCollection(ArrayList.class, keys) + "): "; if (__log.isDebugEnabled()) __log.debug(hdr); diff --git a/jacob/src/main/java/org/apache/ode/jacob/JacobRunnable.java b/jacob/src/main/java/org/apache/ode/jacob/JacobRunnable.java index c177dac..7d8b6c9 100644 --- a/jacob/src/main/java/org/apache/ode/jacob/JacobRunnable.java +++ b/jacob/src/main/java/org/apache/ode/jacob/JacobRunnable.java @@ -23,7 +23,7 @@ import java.lang.reflect.Method; import java.util.Collections; import java.util.Set; -import org.apache.ode.utils.ArrayUtils; +import org.apache.ode.utils.CollectionUtils; /** * Base class for process abstractions. An abstraction is a parameterized @@ -71,7 +71,7 @@ public abstract class JacobRunnable extends JacobObject { static { try { - Method m = JacobRunnable.class.getMethod("run", ArrayUtils.EMPTY_CLASS_ARRAY); + Method m = JacobRunnable.class.getMethod("run", CollectionUtils.EMPTY_CLASS_ARRAY); IMPLEMENTED_METHODS = Collections.singleton(m); } catch (NoSuchMethodException e) { throw new AssertionError(e); diff --git a/jacob/src/main/java/org/apache/ode/jacob/vpu/ChannelFactory.java b/jacob/src/main/java/org/apache/ode/jacob/vpu/ChannelFactory.java index 7b85f60..1608591 100644 --- a/jacob/src/main/java/org/apache/ode/jacob/vpu/ChannelFactory.java +++ b/jacob/src/main/java/org/apache/ode/jacob/vpu/ChannelFactory.java @@ -24,7 +24,7 @@ import java.lang.reflect.Proxy; import org.apache.ode.jacob.Channel; import org.apache.ode.jacob.soup.CommChannel; -import org.apache.ode.utils.ArrayUtils; +import org.apache.ode.utils.CollectionUtils; public class ChannelFactory { private static final Method METHOD_OBJECT_EQUALS; @@ -39,7 +39,7 @@ public class ChannelFactory { } try { - METHOD_CHANNEL_EXPORT = Channel.class.getMethod("export", ArrayUtils.EMPTY_CLASS_ARRAY); + METHOD_CHANNEL_EXPORT = Channel.class.getMethod("export", CollectionUtils.EMPTY_CLASS_ARRAY); } catch (Exception e) { throw new AssertionError("No export() method on Object!"); } diff --git a/jacob/src/main/java/org/apache/ode/jacob/vpu/ExecutionQueueImpl.java b/jacob/src/main/java/org/apache/ode/jacob/vpu/ExecutionQueueImpl.java index 7d82095..9b34b3b 100644 --- a/jacob/src/main/java/org/apache/ode/jacob/vpu/ExecutionQueueImpl.java +++ b/jacob/src/main/java/org/apache/ode/jacob/vpu/ExecutionQueueImpl.java @@ -33,7 +33,7 @@ import org.apache.ode.jacob.soup.Continuation; import org.apache.ode.jacob.soup.ExecutionQueue; import org.apache.ode.jacob.soup.ExecutionQueueObject; import org.apache.ode.jacob.soup.ReplacementMap; -import org.apache.ode.utils.ArrayUtils; +import org.apache.ode.utils.CollectionUtils; import org.apache.ode.utils.ObjectPrinter; import java.io.Externalizable; @@ -142,7 +142,7 @@ public class ExecutionQueueImpl implements ExecutionQueue { public Continuation dequeueReaction() { if (__log.isTraceEnabled()) { - __log.trace(ObjectPrinter.stringifyMethodEnter("dequeueReaction", ArrayUtils.EMPTY_OBJECT_ARRAY)); + __log.trace(ObjectPrinter.stringifyMethodEnter("dequeueReaction", CollectionUtils.EMPTY_OBJECT_ARRAY)); } Continuation continuation = null; @@ -209,7 +209,7 @@ public class ExecutionQueueImpl implements ExecutionQueue { public int cycle() { if (__log.isTraceEnabled()) { - __log.trace(ObjectPrinter.stringifyMethodEnter("cycle", ArrayUtils.EMPTY_OBJECT_ARRAY)); + __log.trace(ObjectPrinter.stringifyMethodEnter("cycle", CollectionUtils.EMPTY_OBJECT_ARRAY)); } return ++_currentCycle; @@ -243,7 +243,7 @@ public class ExecutionQueueImpl implements ExecutionQueue { public void flush() { if (__log.isTraceEnabled()) { - __log.trace(ObjectPrinter.stringifyMethodEnter("flush", ArrayUtils.EMPTY_OBJECT_ARRAY)); + __log.trace(ObjectPrinter.stringifyMethodEnter("flush", CollectionUtils.EMPTY_OBJECT_ARRAY)); } } @@ -622,7 +622,7 @@ public class ExecutionQueueImpl implements ExecutionQueue { public MessageFrame(CommGroupFrame commFrame, ChannelFrame channelFrame, String method, Object[] args) { super(commFrame, channelFrame); this.method = method; - this.args = args == null ? ArrayUtils.EMPTY_CLASS_ARRAY : args; + this.args = args == null ? CollectionUtils.EMPTY_CLASS_ARRAY : args; } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { diff --git a/jacob/src/main/java/org/apache/ode/jacob/vpu/JacobVPU.java b/jacob/src/main/java/org/apache/ode/jacob/vpu/JacobVPU.java index 44787de..2a20fdd 100644 --- a/jacob/src/main/java/org/apache/ode/jacob/vpu/JacobVPU.java +++ b/jacob/src/main/java/org/apache/ode/jacob/vpu/JacobVPU.java @@ -22,7 +22,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.jacob.*; import org.apache.ode.jacob.soup.*; -import org.apache.ode.utils.ArrayUtils; +import org.apache.ode.utils.CollectionUtils; import org.apache.ode.utils.ObjectPrinter; import org.apache.ode.utils.msg.MessageBundle; @@ -57,7 +57,7 @@ public final class JacobVPU { */ static { try { - REDUCE_METHOD = JacobRunnable.class.getMethod("run", ArrayUtils.EMPTY_CLASS_ARRAY); + REDUCE_METHOD = JacobRunnable.class.getMethod("run", CollectionUtils.EMPTY_CLASS_ARRAY); } catch (Exception e) { throw new Error("Cannot resolve 'run' method", e); } @@ -118,7 +118,7 @@ public final class JacobVPU { */ public boolean execute() { if (__log.isTraceEnabled()) { - __log.trace(ObjectPrinter.stringifyMethodEnter("execute", ArrayUtils.EMPTY_OBJECT_ARRAY)); + __log.trace(ObjectPrinter.stringifyMethodEnter("execute", CollectionUtils.EMPTY_OBJECT_ARRAY)); } if (_executionQueue == null) { throw new IllegalStateException("No state object for VPU!"); @@ -151,7 +151,7 @@ public final class JacobVPU { public void flush() { if (__log.isTraceEnabled()) { - __log.trace(ObjectPrinter.stringifyMethodEnter("flush", ArrayUtils.EMPTY_OBJECT_ARRAY)); + __log.trace(ObjectPrinter.stringifyMethodEnter("flush", CollectionUtils.EMPTY_OBJECT_ARRAY)); } _executionQueue.flush(); } @@ -214,7 +214,7 @@ public final class JacobVPU { if (__log.isDebugEnabled()) { __log.debug("injecting " + concretion); } - addReaction(concretion, REDUCE_METHOD, ArrayUtils.EMPTY_OBJECT_ARRAY, + addReaction(concretion, REDUCE_METHOD, CollectionUtils.EMPTY_OBJECT_ARRAY, (__log.isInfoEnabled() ? concretion.toString() : null)); } @@ -313,7 +313,7 @@ public final class JacobVPU { desc = template.toString(); } _statistics.numReductionsStruct++; - addReaction(template, REDUCE_METHOD, ArrayUtils.EMPTY_OBJECT_ARRAY, desc); + addReaction(template, REDUCE_METHOD, CollectionUtils.EMPTY_OBJECT_ARRAY, desc); } public Channel message(Channel channel, Method method, Object[] args) { diff --git a/utils/src/main/java/org/apache/ode/utils/ArrayUtils.java b/utils/src/main/java/org/apache/ode/utils/ArrayUtils.java deleted file mode 100755 index f449cfd..0000000 --- a/utils/src/main/java/org/apache/ode/utils/ArrayUtils.java +++ /dev/null @@ -1,58 +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 java.util.Collection; - -/** - * Utility class for dealing with arrays. - */ -public class ArrayUtils { - - public static final Object[] EMPTY_OBJECT_ARRAY = new Object[]{}; - public static final Class[] EMPTY_CLASS_ARRAY = new Class[]{}; - - /** - * Make a {@link Collection} out of an array. - * - * @param type the type of {@link Collection} to make. - * @param elements objects to put into the collection. - * - * @return a {@link Collection} of the type given in the type argument containing elements - */ - @SuppressWarnings("unchecked") - public static Collection makeCollection(Class type, T[] elements) { - if (elements == null) { - return null; - } - - try { - Collection c = type.newInstance(); - - for (int i = 0; i < elements.length; ++i) { - c.add(elements[i]); - } - - return c; - } catch (Exception ex) { - throw new IllegalArgumentException("Invalid arguments.", ex); - } - } - -} diff --git a/utils/src/main/java/org/apache/ode/utils/CollectionUtils.java b/utils/src/main/java/org/apache/ode/utils/CollectionUtils.java new file mode 100755 index 0000000..3185405 --- /dev/null +++ b/utils/src/main/java/org/apache/ode/utils/CollectionUtils.java @@ -0,0 +1,103 @@ +/* + * 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 java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +/** + * Utility class for dealing with arrays. + */ +public class CollectionUtils { + + public static final Object[] EMPTY_OBJECT_ARRAY = new Object[]{}; + public static final Class[] EMPTY_CLASS_ARRAY = new Class[]{}; + + /** + * Make a {@link Collection} out of an array. + * + * @param type the type of {@link Collection} to make. + * @param elements objects to put into the collection. + * @return a {@link Collection} of the type given in the type argument containing elements + */ + @SuppressWarnings("unchecked") + public static Collection makeCollection(Class type, T[] elements) { + if (elements == null) { + return null; + } + try { + Collection c = type.newInstance(); + + for (int i = 0; i < elements.length; ++i) { + c.add(elements[i]); + } + + return c; + } catch (Exception ex) { + throw new IllegalArgumentException("Invalid arguments.", ex); + } + } + + /** + * Compares the two specified maps for equality. Returns + * true if the two maps represent the same mappings. More formally, two maps m1 and + * m2 represent the same mappings if + * m1.keySet().equals(m2.keySet()) and for every key k + * in m1.keySet(), (m1.get(k)==null ? m2.get(k)==null : + * m1.get(k).equals(m2.get(k))) . + *

+ * This implementation first checks if the m1 and m2 are the same object; + * if so it returns true. Then, it checks if the two maps have the same sizw; if + * not, it returns false. If so, it iterates over m1's + * entrySet collection, and checks that map m1 + * contains each mapping that map m2 contains. If map m1 + * fails to contain such a mapping, false is returned. If the + * iteration completes, true is returned. + * + * @return true if the specified object is equal to this map. + */ + public static boolean equals(Map m1, Map m2) { + if (m2 == m1) return true; + if (m1 == null) return false; + if (m2 == null) return false; + if (m2.size() != m1.size()) return false; + + try { + for (Iterator it = m1.entrySet().iterator(); it.hasNext();) { + Map.Entry e = (Map.Entry) it.next(); + Object key = e.getKey(); + Object value = e.getValue(); + if (value == null) { + if (!(m2.get(key) == null && m2.containsKey(key))) + return false; + } else { + if (!value.equals(m2.get(key))) + return false; + } + } + } catch (ClassCastException unused) { + return false; + } catch (NullPointerException unused) { + return false; + } + return true; + } + +} diff --git a/utils/src/main/java/org/apache/ode/utils/HierarchiedProperties.java b/utils/src/main/java/org/apache/ode/utils/HierarchiedProperties.java new file mode 100644 index 0000000..56f4a6f --- /dev/null +++ b/utils/src/main/java/org/apache/ode/utils/HierarchiedProperties.java @@ -0,0 +1,433 @@ +/* + * 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/main/java/org/apache/ode/utils/ObjectPrinter.java b/utils/src/main/java/org/apache/ode/utils/ObjectPrinter.java index 5c4b9fb..67cacd1 100644 --- a/utils/src/main/java/org/apache/ode/utils/ObjectPrinter.java +++ b/utils/src/main/java/org/apache/ode/utils/ObjectPrinter.java @@ -69,7 +69,7 @@ public class ObjectPrinter { // NOTE: this causes blowups since array may be array of primitive type // which cannot be cast to 'Object[]' // else if (objects[i].getClass().isArray()){ -// buf.append(ArrayUtils.makeCollection(ArrayList.class, (Object[]) objects[i])); +// buf.append(CollectionUtils.makeCollection(ArrayList.class, (Object[]) objects[i])); else buf.append(objects[i]); diff --git a/utils/src/main/java/org/apache/ode/utils/WatchDog.java b/utils/src/main/java/org/apache/ode/utils/WatchDog.java new file mode 100644 index 0000000..fe2f50d --- /dev/null +++ b/utils/src/main/java/org/apache/ode/utils/WatchDog.java @@ -0,0 +1,164 @@ +/* + * 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.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This class is based on {@link org.apache.log4j.helpers.FileWatchdog}.

+ * Modifications have been made to support additional abstract ressource and more events (creation, deletion and updates), and to allow "manual" + * invocations of {@link #check()} (i.e wihtout having to use a thread) while preserving time checking.

+ * Now two use cases coexist: + *

    + *
  1. Pass an instance of {@link WatchDog} to a new thread ({@link WatchDog} is a {@link Runnable}). + * So that {@link WatchDog# check ()} will be called automatically every {@code delay} milliseconds.
  2. + *
  3. Invoke {@link WatchDog# check ()} only when you feel like it. If the expiration date previously set is lower than NOW then event + * callback methods will be invoked accordingly.
  4. + *
+ * + * @author Alexis Midon + */ +public class WatchDog implements Runnable { + static final public long DEFAULT_DELAY = 30000; + final Log log = LogFactory.getLog(getClass()); + + private long expire; + private T lastModif; + private long delay; + private boolean existedBefore, warnedAlready, interrupted; + protected final Mutable mutable; + + /** + * @param mutable the object to watch closely + * @param delay between two checks + */ + public WatchDog(Mutable mutable, long delay) { + this(mutable); + this.delay = delay; + } + + /** + * @see #WatchDog(org.apache.ode.utils.WatchDog.Mutable, long) + */ + public WatchDog(Mutable mutable) { + this.mutable = mutable; + this.delay = DEFAULT_DELAY; + } + + protected boolean isInitialized() { + return true; + } + + /** + * Called by {@link #check()} if the object is not {@link #isInitialized initialized} and the {@link WatchDog.Mutable#exists()} resource does not exist}. + *
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. + *
The default implementation invokes {@link #init()} . + * + * @throws Exception + */ + protected void doOnDelete() { + init(); + } + + /** + * Called only if the resource previously existed but the {@link WatchDog.Mutable#lastModified()} timestamp has changed (greater than the previous value). + *
The default implementation invokes {@link #init()} . + * + * @throws Exception + */ + protected void doOnUpdate() { + init(); + } + + public long getDelay() { + return delay; + } + + public void setDelay(long delay) { + this.delay = delay; + } + + public void run() { + try { + while (!interrupted) { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + // no interruption expected + } + check(); + } + } catch (Exception e) { + log.warn("Exception occured. Thread will stop", e); + } + } + + public final void check() { + long now = System.currentTimeMillis(); + if (expire <= now) { + expire = now + delay; + + if (mutable.exists()) { + existedBefore = true; + if (lastModif==null || mutable.hasChangedSince(lastModif)) { + lastModif = mutable.lastModified(); + if (log.isDebugEnabled()) + log.debug(mutable + " has been modified"); + doOnUpdate(); + warnedAlready = false; + } + } else if (!isInitialized()) { + // no resource and first time + init(); + } else { + if (existedBefore) { + existedBefore = false; + lastModif = null; + doOnDelete(); + } + if (!warnedAlready) { + warnedAlready = true; + if (log.isDebugEnabled()) log.debug(mutable + "] does not exist."); + } + } + } + } + + /** + * have you said that duck typing would be nice? + */ + public interface Mutable { + boolean exists(); + + boolean hasChangedSince(T since); + + T lastModified(); + } + +} diff --git a/utils/src/main/java/org/apache/ode/utils/fs/FileWatchDog.java b/utils/src/main/java/org/apache/ode/utils/fs/FileWatchDog.java index e1f746a..1d28b68 100644 --- a/utils/src/main/java/org/apache/ode/utils/fs/FileWatchDog.java +++ b/utils/src/main/java/org/apache/ode/utils/fs/FileWatchDog.java @@ -19,121 +19,40 @@ package org.apache.ode.utils.fs; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.ode.utils.WatchDog; import java.io.File; -/** - * This class is based on {@link org.apache.log4j.helpers.FileWatchdog}.

- * Modifications have been made to support additional file events (creation, deletion and updates), and to allow "manual" - * invocations of {@link #checkAndConfigure()} (i.e wihtout having to use a thread) while preserving time checking.

- * Now two use cases coexist: - *

    - *
  1. Pass an instance of {@link FileWatchDog} to a new thread ({@link FileWatchDog} is a {@link Runnable}). - * So that {@link FileWatchDog#checkAndConfigure()} will be called automatically every {@code delay} milliseconds.
  2. - *
  3. Invoke {@link FileWatchDog#checkAndConfigure()} only when you feel like it. If the expiration date previously set is lower than NOW then event - * callback methods will be invoked accordingly.
  4. - *
- * - * @author Ceki Gülcü - * @author Alexis Midon - */ -public class FileWatchDog implements Runnable { - static final public long DEFAULT_DELAY = 60000; - private final Log log; +public class FileWatchDog extends WatchDog { - long expire; - long lastModif; - long delay = DEFAULT_DELAY; - boolean fileExistedBefore, warnedAlready, interrupted; protected final File file; - protected FileWatchDog(File file, long delay) { - this(file); - this.delay = delay; - } - - protected FileWatchDog(File file) { + public FileWatchDog(File file, long delay) { + super(new FileDecorator(file),delay); this.file = file; - log = LogFactory.getLog(FileWatchDog.class); - } - - protected boolean isInitialized() throws Exception { - return true; - } - - protected void init() throws Exception { } - protected void doOnDelete() throws Exception { - init(); + public FileWatchDog(File file) { + this(file, WatchDog.DEFAULT_DELAY); } - protected void doOnUpdate() throws Exception { - init(); - } - - public long getDelay() { - return delay; - } - - public void setDelay(long delay) { - this.delay = delay; - } + static class FileDecorator implements WatchDog.Mutable{ + File file; - public void run() { - try { - while (!interrupted) { - try { - Thread.currentThread().sleep(delay); - } catch (InterruptedException e) { - // no interruption expected - } - checkAndConfigure(); - } - } catch (Exception e) { - log.warn("Exception occured. Thread will stop", e); + FileDecorator(File file) { + this.file = file; } - } + public boolean exists() { + return file.exists(); + } - public final void checkAndConfigure() throws Exception { - long now = System.currentTimeMillis(); - if (expire <= now) { - expire = now + delay; - boolean fileExists; - try { - fileExists = file.exists(); - } catch (SecurityException e) { - log.warn("Was not allowed to read check file existance, file:[" + file.getPath() + "]."); - interrupted = true; // there is no point in continuing - return; - } + public boolean hasChangedSince(Long since) { + return lastModified().longValue()>since.longValue(); + } - if (fileExists) { - fileExistedBefore = true; - long l = file.lastModified(); - if (l > lastModif) { - lastModif = l; - if (log.isDebugEnabled()) - log.debug("File [" + file + "] has been modified"); - doOnUpdate(); - warnedAlready = false; - } - } else if (!isInitialized()) { - // first time and no file - init(); - } else { - if (fileExistedBefore) { - fileExistedBefore = false; - doOnDelete(); - } - if (!warnedAlready) { - warnedAlready = true; - if (log.isDebugEnabled()) log.debug("[" + file + "] does not exist."); - } - } + public Long lastModified() { + return Long.valueOf(file.lastModified()); } } diff --git a/utils/src/test/java/org/apache/ode/utils/HierarchiedPropertiesTest.java b/utils/src/test/java/org/apache/ode/utils/HierarchiedPropertiesTest.java new file mode 100644 index 0000000..4d5799b --- /dev/null +++ b/utils/src/test/java/org/apache/ode/utils/HierarchiedPropertiesTest.java @@ -0,0 +1,74 @@ +/* + * 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/java/org/apache/ode/utils/wsdl/WsdlUtilsTest.java b/utils/src/test/java/org/apache/ode/utils/wsdl/WsdlUtilsTest.java index 68613ec..714eb27 100644 --- a/utils/src/test/java/org/apache/ode/utils/wsdl/WsdlUtilsTest.java +++ b/utils/src/test/java/org/apache/ode/utils/wsdl/WsdlUtilsTest.java @@ -42,7 +42,7 @@ import java.util.Map; public class WsdlUtilsTest extends TestCase { private Definition definition; private Service dummyService; - + protected void setUp() throws Exception { super.setUp(); @@ -67,12 +67,7 @@ public class WsdlUtilsTest extends TestCase { public void testEmptyBinding() { Port noBindingPort = dummyService.getPort("DummyService_port_with_empty_binding"); - try { - WsdlUtils.getBindingExtension(noBindingPort); - fail("IllegalArgumentException expected!"); - } catch (IllegalArgumentException e) { - // expected behavior - } + assertNull("should return null", WsdlUtils.getBindingExtension(noBindingPort)); } public void testMultipleBinding() { @@ -177,8 +172,8 @@ public class WsdlUtilsTest extends TestCase { Map.Entry e = (Map.Entry) it.next(); Port port = (Port) e.getValue(); - if("DummyService_port_with_empty_binding".equals(port.getName()) - || "DummyService_port_with_no_binding".equals(port.getName())){ + if ("DummyService_port_with_empty_binding".equals(port.getName()) + || "DummyService_port_with_no_binding".equals(port.getName())) { continue; } diff --git a/utils/src/test/resources/hierarchied.properties b/utils/src/test/resources/hierarchied.properties new file mode 100644 index 0000000..d624cb0 --- /dev/null +++ b/utils/src/test/resources/hierarchied.properties @@ -0,0 +1,10 @@ + + +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? +