001: /*
002: * Portions Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: /*
027: * Copyright (c) 2005 Your Corporation. All Rights Reserved.
028: */
029: package com.sun.xml.internal.ws.client;
030:
031: import com.sun.org.apache.xml.internal.resolver.tools.CatalogResolver;
032: import com.sun.xml.internal.ws.binding.http.HTTPBindingImpl;
033: import com.sun.xml.internal.ws.binding.soap.SOAPBindingImpl;
034: import com.sun.xml.internal.ws.client.dispatch.DispatchBase;
035: import com.sun.xml.internal.ws.handler.PortInfoImpl;
036: import com.sun.xml.internal.ws.model.RuntimeModel;
037: import com.sun.xml.internal.ws.util.xml.XmlUtil;
038: import com.sun.xml.internal.ws.wsdl.WSDLContext;
039: import com.sun.xml.internal.ws.wsdl.parser.Binding;
040: import org.xml.sax.EntityResolver;
041:
042: import javax.xml.bind.JAXBContext;
043: import javax.xml.namespace.QName;
044: import javax.xml.ws.BindingProvider;
045: import javax.xml.ws.Dispatch;
046: import javax.xml.ws.Service;
047: import javax.xml.ws.WebServiceException;
048: import javax.xml.ws.handler.Handler;
049: import javax.xml.ws.handler.HandlerResolver;
050: import javax.xml.ws.handler.PortInfo;
051: import javax.xml.ws.http.HTTPBinding;
052: import javax.xml.ws.soap.SOAPBinding;
053: import javax.xml.ws.spi.ServiceDelegate;
054: import java.lang.reflect.Proxy;
055: import java.net.URL;
056: import java.util.*;
057: import java.util.concurrent.Executor;
058: import java.util.concurrent.Executors;
059: import java.util.concurrent.ThreadFactory;
060:
061: /**
062: * <code>Service</code> objects provide the client view of a Web service.
063: * <p><code>Service</code> acts as a factory of the following:
064: * <ul>
065: * <li>Proxies for a target service endpoint.
066: * <li>Instances of <code>javax.xml.ws.Dispatch</code> for
067: * dynamic message-oriented invocation of a remote
068: * operation.
069: * </li>
070: * <p/>
071: * <p>The ports available on a service can be enumerated using the
072: * <code>getPorts</code> method. Alternatively, you can pass a
073: * service endpoint interface to the unary <code>getPort</code> method
074: * and let the runtime select a compatible port.
075: * <p/>
076: * <p>Handler chains for all the objects created by a <code>Service</code>
077: * can be set by means of the provided <code>HandlerRegistry</code>.
078: * <p/>
079: * <p>An <code>Executor</code> may be set on the service in order
080: * to gain better control over the threads used to dispatch asynchronous
081: * callbacks. For instance, thread pooling with certain parameters
082: * can be enabled by creating a <code>ThreadPoolExecutor</code> and
083: * registering it with the service.
084: *
085: * @author WS Development Team
086: * @see java.util.concurrent.Executor
087: * @since JAX-WS 2.0
088: */
089: public class WSServiceDelegate extends ServiceDelegate {
090:
091: protected static final String GET = "get";
092:
093: protected HashSet<QName> ports;
094:
095: protected HashMap<QName, PortInfoBase> dispatchPorts;
096: protected HandlerResolver handlerResolver;
097:
098: protected Object serviceProxy;
099: protected URL wsdlLocation;
100: protected ServiceContext serviceContext;
101: protected Executor executor;
102: private HashSet<Object> seiProxies;
103:
104: /**
105: * {@link CatalogResolver} to check META-INF/jax-ws-catalog.xml.
106: * Lazily created.
107: */
108: private EntityResolver entityResolver;
109:
110: public WSServiceDelegate(ServiceContext scontext) {
111: serviceContext = scontext;
112: this .dispatchPorts = new HashMap();
113: seiProxies = new HashSet();
114: if (serviceContext.getHandlerResolver() != null) {
115: handlerResolver = serviceContext.getHandlerResolver();
116: }
117: }
118:
119: public WSServiceDelegate(URL wsdlDocumentLocation,
120: QName serviceName, Class serviceClass) {
121: //we cant create a Service without serviceName
122: if (serviceName == null)
123: throw new ClientConfigurationException(
124: "service.noServiceName");
125:
126: this .dispatchPorts = new HashMap();
127: seiProxies = new HashSet();
128:
129: serviceContext = ServiceContextBuilder.build(
130: wsdlDocumentLocation, serviceName, serviceClass,
131: XmlUtil.createDefaultCatalogResolver());
132:
133: if (serviceContext.getHandlerResolver() != null) {
134: handlerResolver = serviceContext.getHandlerResolver();
135: }
136:
137: populatePorts();
138: }
139:
140: private void processServiceContext(QName portName,
141: Class portInterface) throws WebServiceException {
142: ServiceContextBuilder.completeServiceContext(portName,
143: serviceContext, portInterface);
144: }
145:
146: public URL getWSDLLocation() {
147: if (wsdlLocation == null)
148: setWSDLLocation(getWsdlLocation());
149: return wsdlLocation;
150: }
151:
152: public void setWSDLLocation(URL location) {
153: wsdlLocation = location;
154: }
155:
156: public Executor getExecutor() {
157: if (executor != null)
158: //todo:needs to be decoupled from service at execution
159: {
160: return (Executor) executor;
161: } else
162: executor = Executors.newFixedThreadPool(3,
163: new DaemonThreadFactory());
164: return executor;
165: }
166:
167: public void setExecutor(Executor executor) {
168: this .executor = executor;
169: }
170:
171: public HandlerResolver getHandlerResolver() {
172: return handlerResolver;
173: }
174:
175: public void setHandlerResolver(HandlerResolver resolver) {
176: handlerResolver = resolver;
177: }
178:
179: public Object getPort(QName portName, Class portInterface)
180: throws WebServiceException {
181: Object seiProxy = createEndpointIFBaseProxy(portName,
182: portInterface);
183: seiProxies.add(seiProxy);
184: if (portName != null) {
185: addPort(portName);
186: }
187:
188: return seiProxy;
189: }
190:
191: public Object getPort(Class portInterface)
192: throws WebServiceException {
193: return createEndpointIFBaseProxy(null, portInterface);
194: }
195:
196: //todo: rename addPort :spec tbd
197: public void addPort(QName portName, String bindingId,
198: String endpointAddress) throws WebServiceException {
199:
200: if (!dispatchPorts.containsKey(portName)) {
201: dispatchPorts.put(portName, new PortInfoBase(
202: endpointAddress, portName, bindingId));
203: } else
204: throw new WebServiceException(
205: "Port "
206: + portName.toString()
207: + " already exists can not create a port with the same name.");
208: // need to add port to list for HandlerRegistry
209: addPort(portName);
210: }
211:
212: public <T> Dispatch<T> createDispatch(QName qName, Class<T> aClass,
213: Service.Mode mode) throws WebServiceException {
214: return createDispatchClazz(qName, aClass, mode);
215: }
216:
217: public Dispatch<Object> createDispatch(QName qName,
218: JAXBContext jaxbContext, Service.Mode mode)
219: throws WebServiceException {
220: return createDispatchJAXB(qName, jaxbContext, mode);
221: }
222:
223: public QName getServiceName() {
224: return serviceContext.getServiceName();
225: }
226:
227: public Iterator getPorts() throws WebServiceException {
228: if (ports == null)
229: populatePorts();
230:
231: if (ports.size() == 0)
232: throw noWsdlException();
233: return ports.iterator();
234: }
235:
236: public URL getWSDLDocumentLocation() {
237: return getWsdlLocation();
238: }
239:
240: protected void addPorts(QName[] ports) {
241: if (ports != null) {
242: for (int i = 0; i < ports.length; ++i) {
243: addPort(ports[i]);
244: }
245: }
246: }
247:
248: private void populatePorts() {
249: if (ports == null)
250: ports = new HashSet<QName>();
251:
252: WSDLContext wscontext = serviceContext.getWsdlContext();
253:
254: if (serviceContext.getServiceName() == null) {
255: if (wscontext != null) {
256: serviceContext.setServiceName(wscontext
257: .getFirstServiceName());
258: }
259: }
260: Set knownPorts = null;
261:
262: if (wscontext != null) {
263: QName serviceName = serviceContext.getServiceName();
264: knownPorts = wscontext.getPortsAsSet(serviceName);
265: if (knownPorts != null) {
266: QName[] portz = (QName[]) knownPorts
267: .toArray(new QName[knownPorts.size()]);
268: addPorts(portz);
269: for (QName port : portz) {
270: String endpoint = wscontext.getEndpoint(
271: serviceName, port);
272: String bid = wscontext.getWsdlBinding(serviceName,
273: port).getBindingId();
274: dispatchPorts.put(port, new PortInfoBase(endpoint,
275: port, bid));
276: }
277: }
278: }
279: }
280:
281: protected void addPort(QName port) {
282: if (ports == null)
283: populatePorts();
284: ports.add(port);
285: }
286:
287: protected WebServiceException noWsdlException() {
288: return new WebServiceException("dii.service.no.wsdl.available");
289: }
290:
291: private Object createEndpointIFBaseProxy(QName portName,
292: Class portInterface) throws WebServiceException {
293: //fail if service doesnt have WSDL
294: if (serviceContext.getWsdlContext() == null)
295: throw new ClientConfigurationException("service.noWSDLUrl");
296:
297: //if there is portName validate it
298: if ((portName != null)
299: && !serviceContext.getWsdlContext().contains(
300: serviceContext.getServiceName(), portName)) {
301: throw new ClientConfigurationException(
302: "service.invalidPort", portName, serviceContext
303: .getServiceName(), serviceContext
304: .getWsdlContext().getWsdlLocation()
305: .toString());
306: }
307:
308: processServiceContext(portName, portInterface);
309:
310: //if the portName is null it must have been set inside processServiceContext, now get it
311: //from EndpointIfContext
312: if (portName == null)
313: portName = serviceContext.getEndpointIFContext(
314: portInterface.getName()).getPortName();
315:
316: return buildEndpointIFProxy(portName, portInterface);
317: }
318:
319: protected HashSet<QName> getPortsAsSet() {
320: if (ports == null)
321: populatePorts();
322: return ports;
323: }
324:
325: /*
326: * Set the binding on the binding provider. Called by the service
327: * class when creating the binding provider.
328: */
329: protected void setBindingOnProvider(
330: InternalBindingProvider provider, QName portName,
331: String bindingId) {
332:
333: // get handler chain
334: List<Handler> handlerChain = null;
335: HandlerResolver hResolver = getHandlerResolver();
336: if (handlerResolver != null && getServiceName() != null) {
337: PortInfo portInfo = new PortInfoImpl(bindingId, portName,
338: getServiceName());
339: handlerChain = handlerResolver.getHandlerChain(portInfo);
340: } else {
341: handlerChain = new ArrayList<Handler>();
342: }
343:
344: // create binding
345: if (bindingId.toString().equals(SOAPBinding.SOAP11HTTP_BINDING)
346: || bindingId.toString().equals(
347: SOAPBinding.SOAP11HTTP_MTOM_BINDING)
348: || bindingId.toString().equals(
349: SOAPBinding.SOAP12HTTP_BINDING)
350: || bindingId.toString().equals(
351: SOAPBinding.SOAP12HTTP_MTOM_BINDING)) {
352: SOAPBindingImpl bindingImpl = new SOAPBindingImpl(
353: handlerChain, bindingId, getServiceName());
354: Set<String> roles = serviceContext.getRoles(portName);
355: if (roles != null) {
356: bindingImpl.setRoles(roles);
357: }
358: provider._setBinding(bindingImpl);
359: } else if (bindingId.equals(HTTPBinding.HTTP_BINDING)) {
360: provider._setBinding(new HTTPBindingImpl(handlerChain));
361: }
362: }
363:
364: private Dispatch createDispatchClazz(QName port, Class clazz,
365: Service.Mode mode) throws WebServiceException {
366: PortInfoBase dispatchPort = dispatchPorts.get(port);
367: if (dispatchPort != null) {
368: DispatchBase dBase = new DispatchBase(
369: (PortInfoBase) dispatchPort, clazz,
370: (Service.Mode) mode, this );
371: setBindingOnProvider(dBase, port, dBase._getBindingId());
372: return dBase;
373: } else {
374: throw new WebServiceException(
375: "Port must be defined in order to create Dispatch");
376: }
377: }
378:
379: private Dispatch createDispatchJAXB(QName port,
380: JAXBContext jaxbContext, Service.Mode mode)
381: throws WebServiceException {
382: PortInfoBase dispatchPort = dispatchPorts.get(port);
383: if (dispatchPort != null) {
384: DispatchBase dBase = new DispatchBase(
385: (PortInfoBase) dispatchPort, jaxbContext, mode,
386: this );
387: setBindingOnProvider(dBase, port, dBase._getBindingId());
388: return dBase;
389: } else {
390: throw new WebServiceException(
391: "Port must be defined in order to create Dispatch");
392: }
393: }
394:
395: private URL getWsdlLocation() {
396: return serviceContext.getWsdlContext().getWsdlLocation();
397: }
398:
399: private Object buildEndpointIFProxy(QName portQName,
400: Class portInterface) throws WebServiceException {
401:
402: EndpointIFContext eif = completeEndpointIFContext(
403: serviceContext, portQName, portInterface);
404:
405: //apply parameter bindings
406: RuntimeModel model = eif.getRuntimeContext().getModel();
407: if (portQName != null) {
408: Binding binding = getWSDLBinding(portQName);
409: eif.setBindingID(binding.getBindingId());
410: model.applyParameterBinding(binding);
411: }
412:
413: //needs cleaning up
414: EndpointIFInvocationHandler handler = new EndpointIFInvocationHandler(
415: portInterface, eif, this , getServiceName()); //need handler registry passed in here
416: setBindingOnProvider(handler, portQName, handler
417: ._getBindingId());
418:
419: Object proxy = Proxy.newProxyInstance(portInterface
420: .getClassLoader(), new Class[] { portInterface,
421: BindingProvider.class, BindingProviderProperties.class,
422: com.sun.xml.internal.ws.spi.runtime.StubBase.class },
423: handler);
424: handler.setProxy((Object) proxy);
425: return (BindingProvider) proxy;
426: }
427:
428: Binding getWSDLBinding(QName portQName) {
429: return serviceContext.getWsdlContext().getWsdlBinding(
430: serviceContext.getServiceName(), portQName);
431: }
432:
433: private EndpointIFContext completeEndpointIFContext(
434: ServiceContext serviceContext, QName portQName,
435: Class portInterface) {
436:
437: EndpointIFContext context = serviceContext
438: .getEndpointIFContext(portInterface.getName());
439: WSDLContext wscontext = serviceContext.getWsdlContext();
440: if (wscontext != null) {
441: String endpoint = wscontext.getEndpoint(serviceContext
442: .getServiceName(), portQName);
443: String bindingID = wscontext.getBindingID(serviceContext
444: .getServiceName(), portQName);
445: context.setServiceName(serviceContext.getServiceName());
446: context.setPortInfo(portQName, endpoint, bindingID);
447: }
448: return context;
449: }
450:
451: class DaemonThreadFactory implements ThreadFactory {
452: public Thread newThread(Runnable r) {
453: Thread daemonThread = new Thread(r);
454: daemonThread.setDaemon(Boolean.TRUE);
455: return daemonThread;
456: }
457: }
458: }
|