/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.xml.ws.rt; import gw.config.CommonServices; import gw.fs.IFile; import gw.internal.schema.gw.xsd.w3c.soap12_envelope.Envelope; import gw.internal.xml.IXmlLoggerFactory; import gw.internal.xml.config.XmlServices; import gw.internal.xml.ws.AsyncResponseInternal; import gw.internal.xml.ws.IWsdlConfig; import gw.internal.xml.ws.WsdlSoapHeaders; import gw.internal.xml.ws.typeprovider.Wsdl; import gw.internal.xml.ws.typeprovider.WsdlOperationInputInfo; import gw.internal.xml.ws.typeprovider.paraminfo.WsdlOperationParameterInfo; import gw.internal.xml.xsd.typeprovider.IWsdlPortTypeData; import gw.internal.xml.xsd.typeprovider.schema.WsdlPort; import gw.internal.xml.xsd.typeprovider.schemaparser.SoapVersion; import gw.lang.PublishedType; import gw.lang.PublishedTypes; import gw.lang.reflect.IType; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IGosuObject; import gw.lang.reflect.java.JavaTypes; import gw.util.ILogger; import gw.util.Pair; import gw.util.concurrent.LocklessLazyVar; import gw.xml.XmlElement; import gw.xml.XmlSchemaAccess; import gw.xml.ws.WebServiceException; import gw.xml.ws.WsdlFault; import gw.xml.ws.IWsdlPort; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; import javax.xml.namespace.QName; /** * This is the runtime implementation of a web service port, note that if there is a singleton * port then this will also be a wsdl service. A web service port has the operations on it and * is what most people think of as a web service. And that is true if there is a singleton port or * a singleton soap1.2 port. * * This class is not directly exposed, it is meant to be the backing object for the WsdlPortType objects. * * @author dandrews */ @PublishedTypes( @PublishedType(fromType = "gw.internal.xml.ws.IWsdlConfig", toType = "gw.xml.ws.WsdlConfig") ) public class WsdlPortImpl implements IGosuObject, IWsdlPort { final private IType _ownersType; final private QName _portQName; private ILogger _log; final private IWsdlConfig _config; final private String _portTypePackageName; private final WsdlPort _wsdlPort; private final IType _type; private final IWsdlPortTypeData _typeData; final private QName _serviceQName; final protected Wsdl _wsdl; final private IFile _resourceFile; /** * Constructs a port. * * @param ownersType the owner's itype * @param resourceFile the file with the wsdl * @param serviceQName the service qname * @param wsdl the gw wsdl object for this port and service * @param portQName the port's qname * @param config the config * @param wsdlPort the default port */ public WsdlPortImpl( IType ownersType, IFile resourceFile, QName serviceQName, Wsdl wsdl, QName portQName, IWsdlConfig config, WsdlPort wsdlPort ) { _type = ownersType; _typeData = (IWsdlPortTypeData) ownersType; _resourceFile = resourceFile; _serviceQName = serviceQName; _wsdl = wsdl; _wsdlPort = wsdlPort; _portTypePackageName = wsdlPort.getBinding().getPortType().getSchemaIndex().getPackageName(); _log = XmlServices.getLogger( IXmlLoggerFactory.Category.Runtime); _ownersType = ownersType; _portQName = portQName; _config = config; } /** * The type of the owner for this port. * * @return the itype */ public IType getOwnersType() { return _ownersType; } /** * The qname for this port. * * @return the qname */ public QName getPortQName() { return _portQName; } /** * This will get the log for this port * * @return a WSFLogging object */ public ILogger getLogger() { return _log; } /** This will set the logging feature for this port * * @param log a WSFLogging feature (or subclass like WSFJavaLogging or WSFl4jLogging) */ public void setLogger(ILogger log) { if (log == null) { throw new IllegalArgumentException("Log required"); } _log = log; } /** This will invoke an operation * * * @param opTypeData data about the operation, its name, its inputType, its outputType, its unwrapped outputtype * @param args the arguements for the operation @return the returned object if any * @return the object returned from the call */ public Object invoke(final WsdlOperationInfo opTypeData, Object... args) { final long timeBefore = System.currentTimeMillis(); //Calculate the round trip try { AsyncResponseInternal response = invokeAsync( opTypeData, args ); Long callTimeout = _config.getCallTimeout(); if ( callTimeout == null || callTimeout <= 0 ) { response.run(); return response.get(); } else { response.start(); return response.get( callTimeout, TimeUnit.MILLISECONDS ); } } catch ( WsdlFault ex ) { throw ex; } catch ( Exception ex ) { throw new WebServiceException( ex ); } finally { if (CommonServices.getGosuProfilingService() != null) { CommonServices.getGosuProfilingService().completed(timeBefore, System.currentTimeMillis(), _serviceQName == null ? "" : _serviceQName.toString(), opTypeData == null ? "" : opTypeData.getName(), 1, 0); } } } /** This will invoke an operation asynchronously * * * @param opTypeData data about the operation, its name, its inputType, its outputType, its unwrapped outputtype * @param args the arguements for the operation * @return a response object that can be polled for the data */ public AsyncResponseInternal invokeAsync( final WsdlOperationInfo opTypeData, Object... args ) { Pair<XmlElement,List<XmlElement>> pair = createRequestXmlElement(opTypeData, args); List<XmlElement> requestHeaders = pair.getSecond(); List<XmlElement> allHeaders = new ArrayList<XmlElement>(); allHeaders.addAll( _config.getRequestSoapHeaders() ); allHeaders.addAll( requestHeaders ); allHeaders.addAll( _config.getGuidewire().getChildren() ); //noinspection unchecked XmlElement requestXML = pair.getFirst(); XmlElement envelope; SoapVersion soapVersion = _wsdlPort.getBinding().getSoapBinding().getSoapVersion(); if ( soapVersion == SoapVersion.SOAP_12 ) { gw.internal.schema.gw.xsd.w3c.soap12_envelope.Body body = new gw.internal.schema.gw.xsd.w3c.soap12_envelope.Body(); gw.internal.schema.gw.xsd.w3c.soap12_envelope.Envelope requestEnvelope = new gw.internal.schema.gw.xsd.w3c.soap12_envelope.Envelope(); gw.internal.schema.gw.xsd.w3c.soap12_envelope.Header header = null; for ( XmlElement element : allHeaders ) { if ( element == null ) { continue; } if ( header == null ) { header = new gw.internal.schema.gw.xsd.w3c.soap12_envelope.Header(); requestEnvelope.setHeader$( header ); } header.addChild( element ); } requestEnvelope.setBody$( body ); if ( requestXML != null ) { body.addChild( requestXML ); } envelope = requestEnvelope; } else { gw.internal.schema.gw.xsd.w3c.soap11_envelope.Body body = new gw.internal.schema.gw.xsd.w3c.soap11_envelope.Body(); gw.internal.schema.gw.xsd.w3c.soap11_envelope.Envelope requestEnvelope = new gw.internal.schema.gw.xsd.w3c.soap11_envelope.Envelope(); gw.internal.schema.gw.xsd.w3c.soap11_envelope.Header header = null; for ( XmlElement element : allHeaders ) { if ( element == null ) { continue; } if ( header == null ) { header = new gw.internal.schema.gw.xsd.w3c.soap11_envelope.Header(); requestEnvelope.setHeader$( header ); } header.addChild( element ); } requestEnvelope.setBody$( body ); if ( requestXML != null ) { body.addChild( requestXML ); } envelope = requestEnvelope; } return (AsyncResponseInternal) getParameterizedAsyncResponseType( opTypeData, soapVersion, true ).getTypeInfo().getConstructors().get( 0 ).getConstructor().newInstance( opTypeData, this, envelope, getSchemaAccess(), getSoapVersion(), _portTypePackageName ); } static LocklessLazyVar<IType> asyncResponseImpl = new LocklessLazyVar<IType>() { protected IType init() { return TypeSystem.getByFullName("gw.internal.xml.ws.AsyncResponseImpl"); } }; static LocklessLazyVar<IType> asyncResponse = new LocklessLazyVar<IType>() { protected IType init() { return TypeSystem.getByFullName("gw.xml.ws.AsyncResponse"); } }; public static IType getParameterizedAsyncResponseType( WsdlOperationInfo opTypeData, SoapVersion soapVersion, boolean impl ) { IType parameterType; if ( opTypeData == null ) { parameterType = JavaTypes.getJreType( XmlElement.class ); } else if ( opTypeData.getOutputInfo() == null ) { parameterType = JavaTypes.pVOID(); } else { parameterType = opTypeData.getOutputInfo().getReturnType(); } IType envelopeType = soapVersion == SoapVersion.SOAP_12 ? Envelope.TYPE.get() : gw.internal.schema.gw.xsd.w3c.soap11_envelope.Envelope.TYPE.get(); IType responseType = impl ? asyncResponseImpl.get() : asyncResponse.get(); return responseType.getParameterizedType(parameterType, envelopeType); } /** This will take the arguements and wrap them into an xml fragment that can be * included in the request. * * @param inputInfo the input info * @param args the arguements * @return the resulting xml fragment */ Pair<XmlElement,Integer> wrapRequest( WsdlOperationInputInfo inputInfo, Object... args ) { int idx = 0; XmlElement node; if ( ! inputInfo.isUnwrapped() ) { // non-wrapped node = (XmlElement) args[ idx++]; if ( node == null ) { throw new IllegalArgumentException( "Request cannot be null" ); } } else { Iterator<Pair<IType, QName>> iter = inputInfo.getUnwrapLevels().iterator(); if ( iter.hasNext() ) { Pair<IType, QName> pair = iter.next(); while ( true ) { node = (XmlElement) pair.getFirst().getTypeInfo().getConstructor().getConstructor().newInstance(); if ( ! iter.hasNext() ) { break; } pair = iter.next(); } } else { node = (XmlElement) inputInfo.getRequestElementType().getTypeInfo().getConstructor().getConstructor().newInstance(); } for ( Pair<WsdlOperationParameterInfo, Boolean> paramInfo : inputInfo.getParameterInfos() ) { Object arg = args[idx++]; if ( arg != null ) { if ( paramInfo.getSecond() ) { @SuppressWarnings( {"unchecked"} ) List<Object> list = (List<Object>) arg; for ( Object item : list ) { wrapParameter( node, paramInfo, item ); } } else { wrapParameter( node, paramInfo, arg ); } } } } return new Pair<XmlElement,Integer>( node, idx ); } private void wrapParameter( XmlElement node, Pair<WsdlOperationParameterInfo, Boolean> paramInfo, Object arg ) { XmlElement parameterElement = (XmlElement) paramInfo.getFirst().getParameterElementType().getTypeInfo().getConstructor().getConstructor().newInstance(); parameterElement = paramInfo.getFirst().wrap( arg, parameterElement ); node.addChild( parameterElement ); } private Pair<XmlElement,List<XmlElement>> createRequestXmlElement( WsdlOperationInfo opTypeData, Object... args) { if ( opTypeData == null ) { return new Pair<XmlElement, List<XmlElement>>( (XmlElement) args[0], Collections.<XmlElement>emptyList() ); } else { Pair<XmlElement,Integer> pair = wrapRequest( opTypeData.getInputInfo(), args ); XmlElement root = pair.getFirst(); int idx = pair.getSecond(); List<XmlElement> headerElements = new ArrayList<XmlElement>(); if ( idx < args.length ) { WsdlSoapHeaders headers = (WsdlSoapHeaders) args[ idx ]; if ( headers != null ) { for ( XmlElement xmlElement : headers.getAllHeaders().values() ) { headerElements.add( xmlElement ); } } } return new Pair<XmlElement,List<XmlElement>>( root, headerElements ); } } public IWsdlConfig getConfig() { return _config; } public WsdlPort getWsdlPort() { return _wsdlPort; } @Override public IType getIntrinsicType() { return _type; } @Override public String toString() { return _type.getRelativeName() + " Port ( " + _type.getName() + " )"; } /** * This will get the QName of the service * * @return the qname */ public QName getServiceQName() { return _serviceQName; } /** This will get the name space for this service * * @return the name space */ public String getNamespace() { return _wsdl.getPackageName(); } public IFile getResourceFile() { return _resourceFile; } public Wsdl getWsdl() { return _wsdl; } public SoapVersion getSoapVersion() { return getWsdlPort().getBinding().getSoapBinding().getSoapVersion(); } public XmlSchemaAccess getSchemaAccess() { return getWsdl().getXmlSchemaAccess(); } public URI getAddress() { URI url = _config.getServerOverrideUrl(); if ( url == null ) { url = _typeData.getAddress(); } return url; } }