blob: c07d7e1c65b6f4da1da66c966d3fd0dc1e41590e [file] [log] [blame]
/*
* 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.axis2.jaxws.handler;
import org.apache.axis2.jaxws.ExceptionFactory;
import org.apache.axis2.jaxws.core.MessageContext;
import org.apache.axis2.jaxws.description.DescriptionFactory;
import org.apache.axis2.jaxws.description.EndpointDescription;
import org.apache.axis2.jaxws.description.ResolvedHandlersDescription;
import org.apache.axis2.jaxws.description.ServiceDescription;
import org.apache.axis2.jaxws.description.impl.ResolvedHandlersDescriptionImpl;
import org.apache.axis2.jaxws.description.xml.handler.HandlerChainType;
import org.apache.axis2.jaxws.description.xml.handler.HandlerChainsType;
import org.apache.axis2.jaxws.description.xml.handler.HandlerType;
import org.apache.axis2.jaxws.handler.lifecycle.factory.HandlerLifecycleManager;
import org.apache.axis2.jaxws.handler.lifecycle.factory.HandlerLifecycleManagerFactory;
import org.apache.axis2.jaxws.i18n.Messages;
import org.apache.axis2.jaxws.registry.FactoryRegistry;
import org.apache.axis2.jaxws.spi.handler.BaseHandlerResolver;
import org.apache.axis2.util.LoggingControl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.LogicalHandler;
import javax.xml.ws.handler.PortInfo;
import javax.xml.ws.handler.soap.SOAPHandler;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
* This class should be created by the ServiceDelegate.
* HandlerResolverImpl.getHandlerChain(PortInfo) will be called by the
* InvocationContext, and the return value will be set on the Binding
* under the BindingProvider.
*
* HandlerResolverImpl.getHandlerChain(PortInfo) will be responsible for
* starting each Handler's lifecycle according to JAX-WS spec 9.3.1
*/
public class HandlerResolverImpl extends BaseHandlerResolver {
private static Log log = LogFactory.getLog(HandlerResolverImpl.class);
/*
* TODO: is there any value/reason in caching the list we collect from the
* ports? It is a "live" list in the sense that we could possibly return
* a List or ArrayList object to a service or client application, where
* they could manipulate it.
*/
private ServiceDescription serviceDesc;
private Object serviceDelegateKey;
public HandlerResolverImpl(ServiceDescription sd) {
this(sd, null);
}
public HandlerResolverImpl(ServiceDescription sd, Object serviceDelegateKey) {
this.serviceDesc = sd;
this.serviceDelegateKey = serviceDelegateKey;
}
private ResolvedHandlersDescription getResolvedHandlersDescription(PortInfo portInfo) {
ResolvedHandlersDescription resolvedHandlersDesc = null;
// On the client the handler information can be changed via service-delegate-specific
// deployment information. So, only look into the service-side cache only if the
// service delegate key is null.
if (serviceDelegateKey == null) {
resolvedHandlersDesc = serviceDesc.getResolvedHandlersDescription(portInfo);
}
return resolvedHandlersDesc;
}
private ResolvedHandlersDescription getOrCreateResolvedHandlersDescription(PortInfo portInfo) {
ResolvedHandlersDescription resolvedHandlersDesc = null;
// On the client the handler information can be changed via service-delegate-specific
// deployment information. So, only look into the service-side cache only if the
// service delegate key is null.
if (serviceDelegateKey == null) {
resolvedHandlersDesc = serviceDesc.getResolvedHandlersDescription(portInfo);
if (resolvedHandlersDesc == null) {
resolvedHandlersDesc = DescriptionFactory.createResolvedHandlersDescription();
}
}
return resolvedHandlersDesc;
}
private List<Class> geCachedResolvedHandlersClasses(PortInfo portInfo) {
List<Class> cachedHandlerClasses = null;
ResolvedHandlersDescription resolvedHandlersDesc = getResolvedHandlersDescription(portInfo);
if (resolvedHandlersDesc != null) {
cachedHandlerClasses = resolvedHandlersDesc.getHandlerClasses();
}
return cachedHandlerClasses;
}
private void cacheResolvedHandlersInfo(PortInfo portInfo,
List<Class> handlerClasses,
List<String> roles) {
ResolvedHandlersDescription resolvedHandlersDesc = getOrCreateResolvedHandlersDescription(portInfo);
if (resolvedHandlersDesc != null) {
resolvedHandlersDesc.setHandlerClasses(handlerClasses);
resolvedHandlersDesc.setRoles(roles);
serviceDesc.setResolvedHandlersDescription(portInfo, resolvedHandlersDesc);
}
}
private List<Class> getHandlerClasses(PortInfo portInfo) {
List<Class> handlerClasses = geCachedResolvedHandlersClasses(portInfo);
if (handlerClasses == null) {
List<String> resolveRoles = new ArrayList<String>();
handlerClasses = resolveHandlers(portInfo, resolveRoles);
cacheResolvedHandlersInfo(portInfo, handlerClasses, resolveRoles);
}
return handlerClasses;
}
private List<String> getHandlerRoles(PortInfo portInfo) {
List<String> resolveRoles = getCachedResolvedHandlersRoles(portInfo);
if (resolveRoles == null) {
resolveRoles = new ArrayList<String>();
List<Class> resolveClasses = resolveHandlers(portInfo, resolveRoles);
cacheResolvedHandlersInfo(portInfo, resolveClasses, resolveRoles);
}
return resolveRoles;
}
private List<String> getCachedResolvedHandlersRoles(PortInfo portInfo) {
List<String> cachedHandlerRoles = null;
ResolvedHandlersDescription resolvedHandlersDesc = getResolvedHandlersDescription(portInfo);
if (resolvedHandlersDesc != null) {
cachedHandlerRoles = resolvedHandlersDesc.getRoles();
}
return cachedHandlerRoles;
}
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Class> handlerClasses = getHandlerClasses(portInfo);
if (handlerClasses.size() == 0) {
return new ArrayList<Handler>();
}
ArrayList<Handler> handlers = new ArrayList<Handler>();
// Create temporary MessageContext to pass information to HandlerLifecycleManager
MessageContext ctx = new MessageContext();
ctx.setEndpointDescription(serviceDesc.getEndpointDescription(portInfo.getPortName()));
HandlerLifecycleManager hlm = createHandlerlifecycleManager();
for (Iterator<Class> iterator = handlerClasses.iterator(); iterator.hasNext();) {
Class aClass = iterator.next();
// instantiate portHandler class
try {
handlers.add(hlm.createHandlerInstance(ctx, aClass));
} catch (Exception e) {
// TODO: should we just ignore this problem?
throw ExceptionFactory.makeWebServiceException(e);
}
if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) {
log.debug("Successfully instantiated the class: " + aClass);
}
}
return handlers;
}
public List<String> getRoles(PortInfo portInfo) {
List<String> handlerRoles = getHandlerRoles(portInfo);
return handlerRoles;
}
/*
* The list of handlers (rather, list of class names) is already
* available per port. Ports are stored under the ServiceDelegate
* as PortData objects.
*
* The resolveHandlers method is responsible for instantiating each Handler,
* running the annotated PostConstruct method, resolving the list,
* and returning it. We do not sort here.
*/
/**
* Resolve the handlers and roles for the given port. This will process the handler configuration
* information, which can be specified either in a file on a HandlerChain annotaiton or via a
* deployment descriptor. The handler classes and the SOAP roles played will be resolved and
* returned.
*
* @param portinfo Information describing the port for which the handlers and roles are to be
* resolved
* @param handlerRoles OUTPUT PARAMETER! The List that is passed in will be updated with the
* roles relevant to the specfied port.
* @return List of handler classes relevant to the specified port.
* @throws WebServiceException
*/
private ArrayList<Class> resolveHandlers(PortInfo portinfo, List<String> handlerRoles) throws WebServiceException {
/*
A sample XML file for the handler-chains:
<jws:handler-chains xmlns:jws="http://java.sun.com/xml/ns/javaee">
<jws:handler-chain>
<jws:protocol-bindings>##XML_HTTP</jws:protocol-bindings>
<jws:handler>
<jws:handler-name>MyHandler</jws:handler-name>
<jws:handler-class>org.apache.axis2.jaxws.MyHandler</jws:handler-class>
</jws:handler>
</jws:handler-chain>
<jws:handler-chain>
<jws:port-name-pattern>jws:Foo*</jws:port-name-pattern>
<jws:handler>
<jws:handler-name>MyHandler</jws:handler-name>
<jws:handler-class>org.apache.axis2.jaxws.MyHandler</jws:handler-class>
<jws:soap-role>http://org/apache/axis2/jaxws/MyRole</jws:soap-role>
</jws:handler>
</jws:handler-chain>
<jws:handler-chain>
<jws:service-name-pattern>jws:Bar</jws:service-name-pattern>
<jws:handler>
<jws:handler-name>MyHandler</jws:handler-name>
<jws:handler-class>org.apache.axis2.jaxws.MyHandler</jws:handler-class>
</jws:handler>
</jws:handler-chain>
</jws:handler-chains>
Couple of things I'm not sure about...
1) if the protocol-binding, port-name-pattern, and service-name-pattern all
match the PortInfo object, does MyHandler get added three times? Probably would get added 3 times.
2) I assume the asterisk "*" is a wildcard. Can the asterisk only occur on the local part of the qname?
3) Can there be more than one service-name-pattern or port-name-pattern, just like for protocol-bindings?
4) How many protocol-bindings are there? ##XML_HTTP ##SOAP11_HTTP ##SOAP12_HTTP ##SOAP11_HTTP_MTOM ##SOAP12_HTTP_MTOM
They are separated by spaces
*/
// our implementation already has a reference to the EndpointDescription,
// which is where one might get the portinfo object. We still have the
// passed-in variable, however, due to the spec
ArrayList<Class> handlers = new ArrayList<Class>();
/*
* TODO: do a better job checking that the return value matches up
* with the PortInfo object before we add it to the chain.
*/
// The HandlerChain annotation can be specified on:
// - a service implementation (per JSR-181) which is on the service-provider side
// - an SEI (per JSR-181), which can be on both the service-provider and
// service-requester sides
// - a generated Service (per JSR-224), which is on the service-requester side
//
// The order of precedence here is a bit counter intuitive if the HandlerChain annotation
// is present on more than one class.
// - For the service-provider, JSR-181 [p. 25, Section 4.6.1]
// states that the service implementation's HandlerChain takes is used if it is present
// on both the implementation and the SEI.
// - Following that same pattern, we conclude that a generated service HandlerChain should
// take precedence if the annotation is on both the Service and the SEI.
//
// The reasoning for this is (probably) that the SEI can be used by multiple endpoints
// and / or multiple Service requesters, so the endpoint implementation and the Service
// should have the final say in what handlers are run, rather than the SEI.
//
// Adding Deployment Descriptors complicates this further. A DD should have the absolute
// final say (such as a JSR-109 client DD). Given that, on a service-requester if the
// Service has a HandlerChain and the SEI has a HandlerChain and the DD specifies a
// HandlerChain for a port, then the DD should win. Since DDs are implented as information
// in a sparse composite, then that means the sparse composite wins.
// Get the HandlerChains specified on the Endpoint (service-provider) or on the Service
// (service-requester).
handlerChainsType = serviceDesc.getHandlerChain(serviceDelegateKey);
// HandlerChains apply to specific Port Compoments (service-provider) or Ports (
// (service-requesters) so find the appropriate one.
EndpointDescription ed = null;
if(portinfo !=null){
ed = serviceDesc.getEndpointDescription(portinfo.getPortName());
}
// Get the HandlerChain information, if any, off the SEI (service-provider or
// service-requster) and check for any DD overrides.
if (ed != null) {
// If there was no handler chains information specifed on the endpoint (service-
// provider) or the Service (service-requester)
// -- OR --
// If the handler chains associated with a particular instance of a service delegate
// DOES NOT match the handler chains across all service delegates, then there was
// sparse composite information specified for this service delegate. Sparse composite
// information is how Deployment Descriptor information is specified, and that
// overrides the annotations as described in the long-winded comment above.
// -- THEN --
// Use this handler chains information
HandlerChainsType hct_includingComposite = ed.getHandlerChain(serviceDelegateKey);
HandlerChainsType hct_noComposite = ed.getHandlerChain();
if (handlerChainsType == null || (hct_includingComposite != hct_noComposite)) {
handlerChainsType = hct_includingComposite;
}
} else {
// There is no EndpointDescription that matches the portInfo specified so
// return the empty list of handlers since there are no ports that match
if (log.isDebugEnabled()) {
log.debug("The PortInfo object did not match any ports; returning an empty list of handlers."
+ " PortInfo QName: " + portinfo.getPortName());
}
return handlers;
}
Iterator it = handlerChainsType == null ? null : handlerChainsType.getHandlerChain().iterator();
while ((it != null) && (it.hasNext())) {
HandlerChainType handlerChainType = ((HandlerChainType)it.next());
// if !match, continue (to next chain)
if (!(chainResolvesToPort(handlerChainType, portinfo)))
continue;
List<HandlerType> handlerTypeList = handlerChainType.getHandler();
Iterator ht = handlerTypeList.iterator();
while (ht.hasNext()) {
HandlerType handlerType = (HandlerType) ht.next();
// TODO must do better job comparing the handlerType with the PortInfo param
// to see if the current iterator handler is intended for this service.
// TODO review: need to check for null getHandlerClass() return?
// or will schema not allow it?
String portHandler = handlerType.getHandlerClass().getValue();
Class aClass;
try {
aClass = loadClass(portHandler);
} catch (Exception e) {
// TODO: should we just ignore this problem?
throw ExceptionFactory.makeWebServiceException(e);
}
// 9.2.1.2 sort them by Logical, then SOAP
if (LogicalHandler.class.isAssignableFrom(aClass))
handlers.add(aClass);
else if (SOAPHandler.class.isAssignableFrom(aClass))
// instanceof ProtocolHandler
handlers.add(aClass);
else if (Handler.class.isAssignableFrom(aClass)) {
throw ExceptionFactory.makeWebServiceException(Messages
.getMessage("handlerChainErr1", aClass.getName()));
} else {
throw ExceptionFactory.makeWebServiceException(Messages
.getMessage("handlerChainErr2", aClass.getName()));
}
// If a role was specified, add it to the roles played
if (handlerRoles != null) {
List<org.apache.axis2.jaxws.description.xml.handler.String> soapRolesList =
handlerType.getSoapRole();
Iterator<org.apache.axis2.jaxws.description.xml.handler.String>
soapRolesIterator = soapRolesList.iterator();
while (soapRolesIterator.hasNext()) {
org.apache.axis2.jaxws.description.xml.handler.String soapRoleElement =
soapRolesIterator.next();
String addSoapRole = soapRoleElement.getValue();
handlerRoles.add(addSoapRole);
if (log.isDebugEnabled()) {
log.debug("Adding soap role " + addSoapRole);
}
}
}
}
}
return handlers;
}
private HandlerLifecycleManager createHandlerlifecycleManager() {
HandlerLifecycleManagerFactory elmf = (HandlerLifecycleManagerFactory)FactoryRegistry
.getFactory(HandlerLifecycleManagerFactory.class);
return elmf.createHandlerLifecycleManager();
}
}