blob: 4fb6bf6c69addd9b48883ca9dc918a7d7b22fcfd [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.spi.handler;
import org.apache.axis2.java.security.AccessController;
import org.apache.axis2.jaxws.ExceptionFactory;
import org.apache.axis2.jaxws.description.impl.DescriptionUtils;
import org.apache.axis2.jaxws.description.xml.handler.HandlerChainType;
import org.apache.axis2.jaxws.description.xml.handler.HandlerChainsType;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.xml.namespace.QName;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;
import java.io.InputStream;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class can be subclassed to produce different implementations of {@link HandlerResolver}
*
*/
public abstract class BaseHandlerResolver implements HandlerResolver {
private static Log log = LogFactory.getLog(BaseHandlerResolver.class);
// TODO should probably use constants defined elsewhere
protected static final Map<String, String> protocolBindingsMap =
new HashMap<String, String>(5);
protected HandlerChainsType handlerChainsType;
protected BaseHandlerResolver() {
}
protected BaseHandlerResolver(String file) {
ClassLoader classLoader = this.getClass().getClassLoader();
String className = this.getClass().getName();
InputStream is =
DescriptionUtils.openHandlerConfigStream(file, className, classLoader);
if(is == null) {
log.warn("Unable to load handlers from file: " + file);
} else {
handlerChainsType = DescriptionUtils.loadHandlerChains(is, classLoader);
}
}
protected static boolean chainResolvesToPort(HandlerChainType hct, PortInfo portinfo) {
List<String> protocolBindings = hct.getProtocolBindings();
if (protocolBindings != null) {
boolean match = true;
for (Iterator<String> it = protocolBindings.iterator() ; it.hasNext();) {
match = false; // default to false in the protocol bindings until we find a match
String protocolBinding = it.next();
protocolBinding = protocolBinding.startsWith("##") ? protocolBindingsMap.get(protocolBinding) : protocolBinding;
// if the protocolBindingsMap returns null, it would mean someone has some nonsense ##binding
if ((protocolBinding != null) && (protocolBinding.equals(portinfo.getBindingID()))) {
match = true;
break;
}
}
if (match == false) {
// we've checked all the protocolBindings, but didn't find a match, no need to continue
return match;
}
}
/*
* need to figure out how to get the namespace declaration out of the port-name-pattern and service-name-pattern
*/
if (!doesPatternMatch(portinfo.getPortName(), hct.getPortNamePattern())) {
// we've checked the port-name-pattern, and didn't find a match, no need to continue
return false;
}
if (!doesPatternMatch(portinfo.getServiceName(), hct.getServiceNamePattern())) {
// we've checked the service-name-pattern, and didn't find a match, no need to continue
return false;
}
return true;
}
protected static Class loadClass(String clazz) throws ClassNotFoundException {
try {
return forName(clazz, true, getContextClassLoader());
} catch (ClassNotFoundException e) {
throw e;
}
}
/**
* Returns true of the specified qName matches the pattern.
* Some customers may have become dependent on the older
* algorithm. So first the "official" algorithm is used
* and if that fails, the older algorithm is used.
* @param qName QName
* @param pattern QName
* @return true or false
*/
public static boolean doesPatternMatch(QName qName, QName pattern) {
// Validate pattern only generates warnings if the pattern qname
// does not align with the specification.
validatePattern(pattern);
// Try the official pattern match algorithm
boolean match = doesPatternMatch_Official(qName, pattern);
// Customers may be dependent on the old algorithm, so this is retained.
if (!match) {
if (log.isDebugEnabled()) {
log.debug("The offical matching algorithm failed. Re-attempting with the prior algorithm");
}
match = doesPatternMatch_Old(qName, pattern);
if (log.isDebugEnabled()) {
log.debug("The old matching algorithm returns " + match);
}
}
return match;
}
/**
* The old match algorithm combines the namespace and localpart into
* a single string to do the matching. Unfortunately this will cause
* the pure wildcard (*) pattern to fail. And in addition, it may cause
* other patterns to succeed. Prefer the doesPatternMatch_Official algorithm
* @param portInfoQName
* @param pattern
* @return
*/
private static boolean doesPatternMatch_Old(QName portInfoQName, QName pattern) {
if (pattern == null)
return true;
// build up the strings according to the regular expression defined at http://java.sun.com/xml/ns/javaee/javaee_web_services_1_2.xsd
// use the prefix, not the literal namespace
String portInfoPrefix = portInfoQName.getNamespaceURI(); //Prefix();
String portInfoLocalPart = portInfoQName.getLocalPart();
String portInfoString = ((portInfoPrefix == null) || (portInfoPrefix.equals(""))) ? "" : portInfoPrefix + ":";
portInfoString += portInfoLocalPart;
String patternStringPrefix = pattern.getNamespaceURI(); //Prefix();
String patternInfoLocalPart = pattern.getLocalPart();
String patternString = ((patternStringPrefix == null) || (patternStringPrefix.equals(""))) ? "" : patternStringPrefix + ":";
patternString += patternInfoLocalPart;
// now match the portInfoQName to the user pattern
// But first, convert the user pattern to a regular expression. Remember, the only non-QName character allowed is "*", which
// is a wildcard, with obvious restrictions on what characters can match (for example, a .java filename cannot contain perentheses).
// We'll just use part of the above reg ex to form the appropriate restrictions on the user-specified "*" character:
Pattern userp = Pattern.compile(patternString.replace("*", "(\\w|\\.|-|_)*"));
Matcher userm = userp.matcher(portInfoString);
boolean match = userm.matches();
if (log.isDebugEnabled()) {
if (!match) {
log.debug("Pattern match failed: \"" + portInfoString + "\" does not match \"" + patternString + "\"");
} else {
log.debug("Pattern match succeeded: \"" + portInfoString + "\" matches \"" + patternString + "\"");
}
}
return match;
}
/**
* Determine if the indicated qname matches the pattern
* @param qName
* @param pattern
* @return
*/
private static boolean doesPatternMatch_Official(QName qName, QName pattern) {
if (log.isDebugEnabled()) {
log.debug("entry pattern=" + pattern + " qname=" + qName);
}
if (pattern == null) {
if (log.isDebugEnabled()) {
log.debug("Successful Match: Pattern is null");
}
return true;
}
// Do a pattern match on the local part of the qname.
String patternLocalPart = pattern.getLocalPart();
String localPart = qName.getLocalPart();
// Replace the wildcard (*) references in the QName with an appropriate regular expression.
String regEx = patternLocalPart.replace("*", "(\\w|\\.|-|_)*");
Pattern userp = Pattern.compile(regEx);
Matcher userm = userp.matcher(localPart);
boolean match = userm.matches();
if (!match) {
if (log.isDebugEnabled()) {
log.debug("No Match: The local name does not match the regex pattern: " + regEx);
}
return false;
}
// Now do the matching with the namespace.
// If the entire pattern is a wildcard (*), then all namespaces are acceptable.
// For example:
// <port-name-pattern>*</port-name-pattern>
//
// In such cases, the assumption is that the pattern namespace will be empty.
String patternNamespace = pattern.getNamespaceURI();
String namespace = qName.getNamespaceURI();
if (patternNamespace.length() == 0) { // By definition, a namespace will never be null.
if (log.isDebugEnabled()) {
log.debug("Successful Match: The local name matches and the pattern namespace is empty.");
}
return true;
}
// If a namespace is specified, it will be done via a prefix. For example:
// <port-name-pattern>p:MyPortName</port-name-pattern>
// Thus according to the current pattern structure there is no way to
// specify wildcards for the namespace portion of the match
if (patternNamespace.equals(namespace)) {
if (log.isDebugEnabled()) {
log.debug("Successful Match: The local names and namespaces match.");
}
return true;
}
if (log.isDebugEnabled()) {
log.debug("No Match");
}
return false;
}
private static void validatePattern(QName pattern) {
if (pattern == null) {
if (log.isDebugEnabled()) {
log.debug("The pattern qname is null. This is accepted and interpretted as a wildcard");
}
return;
}
String patternStringPrefix = pattern.getPrefix();
String patternInfoLocalPart = pattern.getLocalPart();
String patternString = ((patternStringPrefix == null) || (patternStringPrefix.equals(""))) ? "" : patternStringPrefix + ":";
patternString += patternInfoLocalPart;
/*
* Below pattern is ported from: http://java.sun.com/xml/ns/javaee/javaee_web_services_1_2.xsd
* Schema regular expressions are defined differently from Java regular expressions which are different from Perl regular
* expressions. I've converted the pattern defined in the above linked schema to its Java equivalent, as best as I can.
*
* Schema reg ex: "\*|([\i-[:]][\c-[:]]*:)?[\i-[:]][\c-[:]]*\*?"
* Java reg ex: "\\*|((\\w|_)(\\w|\\.|-|_)*:)?(\\w|_)(\\w|\\.|-|_)*\\*?"
*/
// first, confirm the defined pattern is legal
Pattern p = Pattern.compile("\\*|((\\w|_)(\\w|\\.|-|_)*:)?(\\w|_)(\\w|\\.|-|_)*\\*?");
Matcher m = p.matcher(patternString);
if (!m.matches()) {
// pattern defined by user in handler chain xml file is illegal -- report it but continue
log.warn("Pattern defined by user is illegal: \"" + patternString + "\" does not match regular expression in schema http://java.sun.com/xml/ns/javaee/javaee_web_services_1_2.xsd. Pattern matching should now be considered \"best-effort.\"");
}
}
/**
* Return the class for this name
*
* @return Class
*/
private static Class forName(final String className, final boolean initialize,
final ClassLoader classLoader) throws ClassNotFoundException {
// NOTE: This method must remain protected because it uses AccessController
Class cl = null;
try {
cl = (Class)AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws ClassNotFoundException {
try{
if (log.isDebugEnabled()) {
log.debug("HandlerResolverImpl attempting to load Class: "+className);
}
return Class.forName(className, initialize, classLoader);
} catch (Throwable e) {
// TODO Should the exception be swallowed ?
if (log.isDebugEnabled()) {
log.debug("HandlerResolverImpl cannot load the following class Throwable Exception Occured: " + className);
}
throw new ClassNotFoundException("HandlerResolverImpl cannot load the following class Throwable Exception Occured:" + className);
}
}
}
);
} catch (PrivilegedActionException e) {
if (log.isDebugEnabled()) {
log.debug("Exception thrown from AccessController: " + e);
}
throw (ClassNotFoundException)e.getException();
}
return cl;
}
/** @return ClassLoader */
private static ClassLoader getContextClassLoader() {
// NOTE: This method must remain private because it uses AccessController
ClassLoader cl = null;
try {
cl = (ClassLoader)AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws ClassNotFoundException {
return Thread.currentThread().getContextClassLoader();
}
}
);
} catch (PrivilegedActionException e) {
if (log.isDebugEnabled()) {
log.debug("Exception thrown from AccessController: " + e);
}
throw ExceptionFactory.makeWebServiceException(e.getException());
}
return cl;
}
static {
protocolBindingsMap.put("##SOAP11_HTTP", "http://schemas.xmlsoap.org/wsdl/soap/http");
protocolBindingsMap.put("##SOAP11_HTTP_MTOM", "http://schemas.xmlsoap.org/wsdl/soap/http?mtom=true");
protocolBindingsMap.put("##SOAP12_HTTP", "http://www.w3.org/2003/05/soap/bindings/HTTP/");
protocolBindingsMap.put("##SOAP12_HTTP_MTOM", "http://www.w3.org/2003/05/soap/bindings/HTTP/?mtom=true");
protocolBindingsMap.put("##XML_HTTP", "http://www.w3.org/2004/08/wsdl/http");
}
}