001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.axis2.jaxws.handler;
020:
021: import org.apache.axis2.client.OperationClient;
022: import org.apache.axis2.java.security.AccessController;
023: import org.apache.axis2.jaxws.ExceptionFactory;
024: import org.apache.axis2.jaxws.core.MessageContext;
025: import org.apache.axis2.jaxws.description.EndpointDescription;
026: import org.apache.axis2.jaxws.description.ServiceDescription;
027: import org.apache.axis2.jaxws.description.xml.handler.HandlerChainType;
028: import org.apache.axis2.jaxws.description.xml.handler.HandlerChainsType;
029: import org.apache.axis2.jaxws.description.xml.handler.HandlerType;
030: import org.apache.axis2.jaxws.handler.lifecycle.factory.HandlerLifecycleManager;
031: import org.apache.axis2.jaxws.handler.lifecycle.factory.HandlerLifecycleManagerFactory;
032: import org.apache.axis2.jaxws.i18n.Messages;
033: import org.apache.axis2.jaxws.registry.FactoryRegistry;
034: import org.apache.axis2.jaxws.runtime.description.injection.ResourceInjectionServiceRuntimeDescription;
035: import org.apache.axis2.jaxws.runtime.description.injection.impl.ResourceInjectionServiceRuntimeDescriptionBuilder;
036: import org.apache.commons.logging.Log;
037: import org.apache.commons.logging.LogFactory;
038:
039: import javax.xml.namespace.QName;
040: import javax.xml.ws.WebServiceException;
041: import javax.xml.ws.handler.Handler;
042: import javax.xml.ws.handler.HandlerResolver;
043: import javax.xml.ws.handler.LogicalHandler;
044: import javax.xml.ws.handler.PortInfo;
045: import javax.xml.ws.handler.soap.SOAPHandler;
046:
047: import java.lang.reflect.InvocationTargetException;
048: import java.lang.reflect.Method;
049: import java.security.PrivilegedActionException;
050: import java.security.PrivilegedExceptionAction;
051: import java.util.ArrayList;
052: import java.util.HashMap;
053: import java.util.Iterator;
054: import java.util.List;
055: import java.util.Map;
056:
057: /*
058: * This class should be created by the ServiceDelegate.
059: * HandlerResolverImpl.getHandlerChain(PortInfo) will be called by the
060: * InvocationContext, and the return value will be set on the Binding
061: * under the BindingProvider.
062: *
063: * HandlerResolverImpl.getHandlerChain(PortInfo) will be responsible for
064: * starting each Handler's lifecycle according to JAX-WS spec 9.3.1
065: */
066:
067: public class HandlerResolverImpl implements HandlerResolver {
068:
069: // TODO should probably use constants defined elsewhere
070: static final Map<String, String> protocolBindingsMap = new HashMap<String, String>(
071: 5);
072: static {
073: protocolBindingsMap.put("##SOAP11_HTTP",
074: "http://schemas.xmlsoap.org/wsdl/soap/http");
075: protocolBindingsMap.put("##SOAP11_HTTP_MTOM",
076: "http://schemas.xmlsoap.org/wsdl/soap/http?mtom=true");
077: protocolBindingsMap.put("##SOAP12_HTTP",
078: "http://www.w3.org/2003/05/soap/bindings/HTTP/");
079: protocolBindingsMap
080: .put("##SOAP12_HTTP_MTOM",
081: "http://www.w3.org/2003/05/soap/bindings/HTTP/?mtom=true");
082: protocolBindingsMap.put("##XML_HTTP",
083: "http://www.w3.org/2004/08/wsdl/http");
084: }
085: private static Log log = LogFactory
086: .getLog(HandlerResolverImpl.class);
087: /*
088: * TODO: is there any value/reason in caching the list we collect from the
089: * ports? It is a "live" list in the sense that we could possibly return
090: * a List or ArrayList object to a service or client application, where
091: * they could manipulate it.
092: */
093:
094: // we'll need to refer to this object to get the port, and thus handlers
095: //private EndpointDescription endpointDesc;
096: private ServiceDescription serviceDesc;
097:
098: public HandlerResolverImpl(ServiceDescription sd) { //EndpointDescription ed) {
099: //this.endpointDesc = ed;
100: this .serviceDesc = sd;
101: }
102:
103: public ArrayList<Handler> getHandlerChain(PortInfo portinfo) {
104: // TODO: would check and/or build cache here if implemented later
105: return resolveHandlers(portinfo);
106: }
107:
108: /*
109: * The list of handlers (rather, list of class names) is already
110: * available per port. Ports are stored under the ServiceDelegate
111: * as PortData objects.
112: *
113: * The resolveHandlers method is responsible for instantiating each Handler,
114: * running the annotated PostConstruct method, resolving the list,
115: * and returning it. We do not sort here.
116: */
117: private ArrayList<Handler> resolveHandlers(PortInfo portinfo)
118: throws WebServiceException {
119: /*
120:
121: A sample XML file for the handler-chains:
122:
123: <jws:handler-chains xmlns:jws="http://java.sun.com/xml/ns/javaee">
124: <jws:handler-chain>
125: <jws:protocol-bindings>##XML_HTTP</jws:protocol-bindings>
126: <jws:handler>
127: <jws:handler-name>MyHandler</jws:handler-name>
128: <jws:handler-class>org.apache.axis2.jaxws.MyHandler</jws:handler-class>
129: </jws:handler>
130: </jws:handler-chain>
131: <jws:handler-chain>
132: <jws:port-name-pattern>jws:Foo*</jws:port-name-pattern>
133: <jws:handler>
134: <jws:handler-name>MyHandler</jws:handler-name>
135: <jws:handler-class>org.apache.axis2.jaxws.MyHandler</jws:handler-class>
136: </jws:handler>
137: </jws:handler-chain>
138: <jws:handler-chain>
139: <jws:service-name-pattern>jws:Bar</jws:service-name-pattern>
140: <jws:handler>
141: <jws:handler-name>MyHandler</jws:handler-name>
142: <jws:handler-class>org.apache.axis2.jaxws.MyHandler</jws:handler-class>
143: </jws:handler>
144: </jws:handler-chain>
145: </jws:handler-chains>
146:
147: Couple of things I'm not sure about...
148: 1) if the protocol-binding, port-name-pattern, and service-name-pattern all
149: match the PortInfo object, does MyHandler get added three times? Probably would get added 3 times.
150: 2) I assume the asterisk "*" is a wildcard. Can the asterisk only occur on the local part of the qname?
151: 3) Can there be more than one service-name-pattern or port-name-pattern, just like for protocol-bindings?
152: 4) How many protocol-bindings are there? ##XML_HTTP ##SOAP11_HTTP ##SOAP12_HTTP ##SOAP11_HTTP_MTOM ##SOAP12_HTTP_MTOM
153: They are separated by spaces
154: */
155:
156: // our implementation already has a reference to the EndpointDescription,
157: // which is where one might get the portinfo object. We still have the
158: // passed-in variable, however, due to the spec
159: ArrayList<Handler> handlers = new ArrayList<Handler>();
160:
161: /*
162: * TODO: do a better job checking that the return value matches up
163: * with the PortInfo object before we add it to the chain.
164: */
165:
166: HandlerChainsType handlerCT = serviceDesc.getHandlerChain();
167: // if there's a handlerChain on the serviceDesc, it means the WSDL defined an import for a HandlerChain.
168: // the spec indicates that if a handlerchain also appears on the SEI on the client.
169: EndpointDescription ed = null;
170: if (portinfo != null) {
171: ed = serviceDesc.getEndpointDescription(portinfo
172: .getPortName());
173: }
174:
175: if (ed != null) {
176: HandlerChainsType handlerCT_fromEndpointDesc = ed
177: .getHandlerChain();
178: if (handlerCT == null) {
179: handlerCT = handlerCT_fromEndpointDesc;
180: }
181: }
182:
183: Iterator it = handlerCT == null ? null : handlerCT
184: .getHandlerChain().iterator();
185:
186: while ((it != null) && (it.hasNext())) {
187: HandlerChainType handlerChainType = ((HandlerChainType) it
188: .next());
189:
190: // if !match, continue (to next chain)
191: if (!(chainResolvesToPort(handlerChainType, portinfo)))
192: continue;
193:
194: List<HandlerType> handlerTypeList = handlerChainType
195: .getHandler();
196: Iterator ht = handlerTypeList.iterator();
197: while (ht.hasNext()) {
198:
199: HandlerType handlerType = (HandlerType) ht.next();
200:
201: // TODO must do better job comparing the handlerType with the PortInfo param
202: // to see if the current iterator handler is intended for this service.
203:
204: // TODO review: need to check for null getHandlerClass() return?
205: // or will schema not allow it?
206: String portHandler = handlerType.getHandlerClass()
207: .getValue();
208: Handler handler;
209: // Create temporary MessageContext to pass information to HandlerLifecycleManager
210: MessageContext ctx = new MessageContext();
211: ctx.setEndpointDescription(ed);
212:
213: HandlerLifecycleManager hlm = createHandlerlifecycleManager();
214:
215: // instantiate portHandler class
216: try {
217: handler = hlm.createHandlerInstance(ctx,
218: loadClass(portHandler));
219: } catch (Exception e) {
220: // TODO: should we just ignore this problem?
221: // TODO: NLS log and throw
222: throw ExceptionFactory.makeWebServiceException(e);
223: }
224: // 9.2.1.2 sort them by Logical, then SOAP
225: if (LogicalHandler.class.isAssignableFrom(handler
226: .getClass()))
227: handlers.add((LogicalHandler) handler);
228: else if (SOAPHandler.class.isAssignableFrom(handler
229: .getClass()))
230: // instanceof ProtocolHandler
231: handlers.add((SOAPHandler) handler);
232: else if (Handler.class.isAssignableFrom(handler
233: .getClass())) {
234: // TODO: NLS better error message
235: throw ExceptionFactory
236: .makeWebServiceException(Messages
237: .getMessage("handlerChainErr1",
238: handler.getClass()
239: .getName()));
240: } else {
241: // TODO: NLS better error message
242: throw ExceptionFactory
243: .makeWebServiceException(Messages
244: .getMessage("handlerChainErr2",
245: handler.getClass()
246: .getName()));
247: }
248: }
249: }
250:
251: return handlers;
252: }
253:
254: private HandlerLifecycleManager createHandlerlifecycleManager() {
255: HandlerLifecycleManagerFactory elmf = (HandlerLifecycleManagerFactory) FactoryRegistry
256: .getFactory(HandlerLifecycleManagerFactory.class);
257: return elmf.createHandlerLifecycleManager();
258: }
259:
260: private static Class loadClass(String clazz)
261: throws ClassNotFoundException {
262: try {
263: return forName(clazz, true, getContextClassLoader());
264: } catch (ClassNotFoundException e) {
265: throw e;
266: }
267: }
268:
269: /**
270: * Return the class for this name
271: *
272: * @return Class
273: */
274: private static Class forName(final String className,
275: final boolean initialize, final ClassLoader classLoader)
276: throws ClassNotFoundException {
277: // NOTE: This method must remain protected because it uses AccessController
278: Class cl = null;
279: try {
280: cl = (Class) AccessController
281: .doPrivileged(new PrivilegedExceptionAction() {
282: public Object run()
283: throws ClassNotFoundException {
284: try {
285: if (log.isDebugEnabled()) {
286: log
287: .debug("HandlerResolverImpl attempting to load Class: "
288: + className);
289: }
290: return Class.forName(className,
291: initialize, classLoader);
292: } catch (Throwable e) {
293: // TODO Should the exception be swallowed ?
294: if (log.isDebugEnabled()) {
295: log
296: .debug("HandlerResolverImpl cannot load the following class Throwable Exception Occured: "
297: + className);
298: }
299: throw new ClassNotFoundException(
300: "HandlerResolverImpl cannot load the following class Throwable Exception Occured:"
301: + className);
302: }
303: }
304: });
305: } catch (PrivilegedActionException e) {
306: if (log.isDebugEnabled()) {
307: log.debug("Exception thrown from AccessController: "
308: + e);
309: }
310: throw (ClassNotFoundException) e.getException();
311: }
312:
313: return cl;
314: }
315:
316: /** @return ClassLoader */
317: private static ClassLoader getContextClassLoader() {
318: // NOTE: This method must remain private because it uses AccessController
319: ClassLoader cl = null;
320: try {
321: cl = (ClassLoader) AccessController
322: .doPrivileged(new PrivilegedExceptionAction() {
323: public Object run()
324: throws ClassNotFoundException {
325: return Thread.currentThread()
326: .getContextClassLoader();
327: }
328: });
329: } catch (PrivilegedActionException e) {
330: if (log.isDebugEnabled()) {
331: log.debug("Exception thrown from AccessController: "
332: + e);
333: }
334: throw ExceptionFactory.makeWebServiceException(e
335: .getException());
336: }
337:
338: return cl;
339: }
340:
341: private static boolean chainResolvesToPort(
342: HandlerChainType handlerChainType, PortInfo portinfo) {
343:
344: List<String> protocolBindings = handlerChainType
345: .getProtocolBindings();
346: if (protocolBindings != null) {
347: boolean match = true;
348: for (Iterator<String> it = protocolBindings.iterator(); it
349: .hasNext();) {
350: match = false; // default to false in the protocol bindings until we find a match
351: String protocolBinding = it.next();
352: protocolBinding = protocolBinding.startsWith("##") ? protocolBindingsMap
353: .get(protocolBinding)
354: : protocolBinding;
355: // if the protocolBindingsMap returns null, it would mean someone has some nonsense ##binding
356: if ((protocolBinding != null)
357: && (protocolBinding.equals(portinfo
358: .getBindingID()))) {
359: match = true;
360: break;
361: }
362: }
363: if (match == false) {
364: // we've checked all the protocolBindings, but didn't find a match, no need to continue
365: return match;
366: }
367: }
368:
369: /*
370: * need to figure out how to get the namespace declaration out of the port-name-pattern and service-name-pattern
371: */
372:
373: if (!doesPatternMatch(portinfo.getPortName(), handlerChainType
374: .getPortNamePattern())) {
375: // we've checked the port-name-pattern, and didn't find a match, no need to continue
376: return false;
377: }
378:
379: if (!doesPatternMatch(portinfo.getServiceName(),
380: handlerChainType.getServiceNamePattern())) {
381: // we've checked the service-name-pattern, and didn't find a match, no need to continue
382: return false;
383: }
384:
385: return true;
386: }
387:
388: /*
389: * A comparison routing to check service-name-pattern and port-name-pattern. These patterns may be of
390: * the form:
391: *
392: * 1) namespace:localpart
393: * 2) namespace:localpart*
394: * 3) namespace:* (not sure about this one)
395: * 4) * (which is equivalent to not specifying a pattern, therefore always matching)
396: *
397: * I've not seen any examples where the wildcard may be placed mid-string or on the namespace, such as:
398: *
399: * namespace:local*part
400: * *:localpart
401: *
402: */
403: private static boolean doesPatternMatch(QName portInfoQName,
404: QName pattern) {
405: if (pattern == null)
406: return true;
407: String portInfoString = portInfoQName.toString();
408: String patternString = pattern.toString();
409: if (patternString.equals("*"))
410: return true;
411: if (patternString.contains("*")) {
412: patternString = patternString.substring(0, patternString
413: .length() - 1);
414: return portInfoString.startsWith(patternString);
415: }
416: return portInfoString.equals(patternString);
417:
418: }
419:
420: }
|