All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.internal.dbws.ProviderHelper Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink

package org.eclipse.persistence.internal.dbws;

//javase imports

import static jakarta.xml.soap.SOAPConstants.SOAP_1_2_PROTOCOL;
import static jakarta.xml.soap.SOAPConstants.URI_NS_SOAP_1_1_ENVELOPE;
import static jakarta.xml.soap.SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE;
import static jakarta.xml.ws.handler.MessageContext.INBOUND_MESSAGE_ATTACHMENTS;
import static org.eclipse.persistence.internal.dbws.SOAPResponseWriter.RECEIVER_QNAME;
import static org.eclipse.persistence.internal.dbws.SOAPResponseWriter.SERVER_QNAME;
import static org.eclipse.persistence.internal.xr.Util.DBWS_SCHEMA_XML;
import static org.eclipse.persistence.internal.xr.Util.DBWS_SERVICE_XML;
import static org.eclipse.persistence.internal.xr.Util.DBWS_WSDL;
import static org.eclipse.persistence.internal.xr.Util.META_INF_PATHS;
import static org.eclipse.persistence.internal.xr.Util.SCHEMA_2_CLASS;
import static org.eclipse.persistence.internal.xr.Util.SERVICE_NAMESPACE_PREFIX;
import static org.eclipse.persistence.internal.xr.Util.WEB_INF_DIR;
import static org.eclipse.persistence.internal.xr.Util.WSDL_DIR;
import static org.eclipse.persistence.oxm.mappings.UnmarshalKeepAsElementPolicy.KEEP_UNKNOWN_AS_ELEMENT;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import jakarta.activation.DataHandler;
import jakarta.servlet.ServletContext;
import javax.xml.namespace.QName;
import jakarta.xml.soap.SOAPBodyElement;
import jakarta.xml.soap.SOAPElement;
import jakarta.xml.soap.SOAPEnvelope;
import jakarta.xml.soap.SOAPException;
import jakarta.xml.soap.SOAPFactory;
import jakarta.xml.soap.SOAPFault;
import jakarta.xml.soap.SOAPMessage;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import jakarta.xml.ws.Provider;
import jakarta.xml.ws.WebServiceException;
import jakarta.xml.ws.handler.MessageContext;
import jakarta.xml.ws.soap.SOAPFaultException;

import org.eclipse.persistence.dbws.DBWSModelProject;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.DBWSException;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.oxm.XMLConversionManager;
import org.eclipse.persistence.internal.oxm.schema.SchemaModelProject;
import org.eclipse.persistence.internal.oxm.schema.model.ComplexType;
import org.eclipse.persistence.internal.oxm.schema.model.Schema;
import org.eclipse.persistence.internal.sessions.DatabaseSessionImpl;
import org.eclipse.persistence.internal.xr.Invocation;
import org.eclipse.persistence.internal.xr.Operation;
import org.eclipse.persistence.internal.xr.Parameter;
import org.eclipse.persistence.internal.xr.ValueObject;
import org.eclipse.persistence.internal.xr.XRServiceAdapter;
import org.eclipse.persistence.internal.xr.XRServiceFactory;
import org.eclipse.persistence.internal.xr.XRServiceModel;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.mappings.AttributeAccessor;
import org.eclipse.persistence.oxm.NamespaceResolver;
import org.eclipse.persistence.oxm.XMLContext;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.XMLRoot;
import org.eclipse.persistence.oxm.XMLUnmarshaller;
import org.eclipse.persistence.oxm.attachment.XMLAttachmentUnmarshaller;
import org.eclipse.persistence.oxm.mappings.XMLAnyCollectionMapping;
import org.eclipse.persistence.oxm.schema.XMLSchemaReference;
import org.eclipse.persistence.sessions.Project;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

// Java extension imports
// EclipseLink imports

/**
 * 

* INTERNAL: ProviderHelper bridges between {@link DBWSAdapter}'s and JAX-WS {@link Provider}'s * * @author Mike Norman - [email protected] * @since EclipseLink 1.1 *

 * packaging required for deployment as a Web Service
 * \--- root of war file
 *      |
 *      \---web-inf
 *          |   web.xml
 *          |
 *          +---classes
 *          |   +---META-INF
 *          |   |    eclipselink-dbws.xml
 *          |   |    eclipselink-dbws-sessions.xml -- name can be overridden by <sessions-file> entry in eclipselink-dbws.xml
 *          |   |    eclipselink-dbws-or.xml
 *          |   |    eclipselink-dbws-ox.xml
 *          |   |
 *          |   +---_dbws
 *          |   |    DBWSProvider.java         -- (source provided as a convenience for IDE integration)
 *          |   |    DBWSProvider.class        -- ASM-generated jakarta.xml.ws.Provider
 *          |   |
 *          |   \---foo                        -- optional domain classes
 *          |       \---bar
 *          |             Address.class
 *          |             Employee.class
 *          |             PhoneNumber.class
 *          \---wsdl
 *                 swaref.xsd                  -- optional to handle attachments
 *                 eclipselink-dbws.wsdl
 *                 eclipselink-dbws-schema.xsd
 * 
*/ public class ProviderHelper extends XRServiceFactory { public static final QName SENDER_QNAME = new QName(URI_NS_SOAP_1_2_ENVELOPE, "Sender"); public static final QName CLIENT_QNAME = new QName(URI_NS_SOAP_1_1_ENVELOPE, "Client"); protected static final String XSL_PREAMBLE = " " + " " + " "; protected static final String XSL_POSTSCRIPT = ""; public static final String MATCH_SCHEMA = XSL_PREAMBLE + "" + "" + "" + "" + "" + "" + XSL_POSTSCRIPT; public SOAPResponseWriter responseWriter = null; protected boolean mtomEnabled; protected MessageContext mc; // Default constructor required by servlet/jax-ws spec public ProviderHelper() { super(); } protected void setMessageContext(MessageContext mc) { this.mc = mc; } protected InputStream initXRServiceStream(ClassLoader parentClassLoader, @SuppressWarnings("unused") ServletContext sc) { InputStream xrServiceStream = null; for (String searchPath : META_INF_PATHS) { String path = searchPath + DBWS_SERVICE_XML; xrServiceStream = parentClassLoader.getResourceAsStream(path); if (xrServiceStream != null) { break; } } if (xrServiceStream == null) { throw new WebServiceException(DBWSException.couldNotLocateFile(DBWS_SERVICE_XML)); } return xrServiceStream; } protected InputStream initXRSchemaStream(ClassLoader parentClassLoader, ServletContext sc) { InputStream xrSchemaStream; String path = WSDL_DIR + DBWS_SCHEMA_XML; if (sc != null) { path = "/" + WEB_INF_DIR + path; xrSchemaStream = sc.getResourceAsStream(path); } else { // if ServletContext is null, then we are running in JavaSE6 'container-less' mode xrSchemaStream = parentClassLoader.getResourceAsStream(path); } if (xrSchemaStream == null) { throw new WebServiceException(DBWSException.couldNotLocateFile(DBWS_SCHEMA_XML)); } return xrSchemaStream; } protected InputStream initWSDLInputStream(ClassLoader parentClassLoader, ServletContext sc) { InputStream wsdlInputStream; String path = WSDL_DIR + DBWS_WSDL; if (sc != null) { path = "/" + WEB_INF_DIR + path; wsdlInputStream = sc.getResourceAsStream(path); } else { // if ServletContext is null, then we are running in JavaSE6 'container-less' mode wsdlInputStream = parentClassLoader.getResourceAsStream(path); } if (wsdlInputStream == null) { throw new WebServiceException(DBWSException.couldNotLocateFile(DBWS_WSDL)); } return wsdlInputStream; } @SuppressWarnings("unchecked") public void init(ClassLoader parentClassLoader, ServletContext sc, boolean mtomEnabled) { this.parentClassLoader = parentClassLoader; this.mtomEnabled = mtomEnabled; InputStream xrServiceStream = initXRServiceStream(parentClassLoader, sc); DBWSModelProject xrServiceModelProject = new DBWSModelProject(); XMLContext xmlContext = new XMLContext(xrServiceModelProject); XMLUnmarshaller unmarshaller = xmlContext.createUnmarshaller(); XRServiceModel xrServiceModel; try { xrServiceModel = (XRServiceModel)unmarshaller.unmarshal(xrServiceStream); } catch (XMLMarshalException e) { // something went wrong parsing the eclipselink-dbws.xml - can't recover from that throw new WebServiceException(DBWSException.couldNotParseDBWSFile()); } finally { try { xrServiceStream.close(); } catch (IOException e) { // ignore } } xrSchemaStream = initXRSchemaStream(parentClassLoader, sc); try { buildService(xrServiceModel); // inherit xrService processing from XRServiceFactory } catch (Exception e) { // something went wrong building the service throw new WebServiceException(e); } // the xrService built by 'buildService' above is overridden to produce an // instance of DBWSAdapter (a sub-class of XRService) DBWSAdapter dbwsAdapter = (DBWSAdapter)xrService; // get inline schema from WSDL - has additional types for the operations try (InputStream wsdlInputStream = initWSDLInputStream(parentClassLoader, sc)) { StringWriter sw = new StringWriter(); StreamSource wsdlStreamSource = new StreamSource(wsdlInputStream); Transformer t = TransformerFactory.newInstance().newTransformer(new StreamSource( new StringReader(MATCH_SCHEMA))); StreamResult streamResult = new StreamResult(sw); t.transform(wsdlStreamSource, streamResult); SchemaModelProject schemaProject = new SchemaModelProject(); XMLContext xmlContext2 = new XMLContext(schemaProject); unmarshaller = xmlContext2.createUnmarshaller(); Schema extendedSchema = (Schema)unmarshaller.unmarshal(new StringReader(sw.toString())); dbwsAdapter.setExtendedSchema(extendedSchema); } catch (IOException | TransformerException e) { // that's Ok, WSDL may not contain inline schema xmlContext.getSession().getSessionLog().log( SessionLog.FINE, SessionLog.DBWS, "dbws_no_wsdl_inline_schema", e.getLocalizedMessage()); } // an Invocation needs a mapping for its parameters - use XMLAnyCollectionMapping + // custom AttributeAccessor // NB - this code is NOt in it own initNNN method, cannot be overridden String tns = dbwsAdapter.getExtendedSchema().getTargetNamespace(); Project oxProject = dbwsAdapter.getOXSession().getProject(); XMLDescriptor invocationDescriptor = new XMLDescriptor(); invocationDescriptor.setJavaClass(Invocation.class); NamespaceResolver nr = new NamespaceResolver(); invocationDescriptor.setNamespaceResolver(nr); nr.put(SERVICE_NAMESPACE_PREFIX, tns); nr.setDefaultNamespaceURI(tns); XMLAnyCollectionMapping parametersMapping = new XMLAnyCollectionMapping(); parametersMapping.setAttributeName("parameters"); parametersMapping.setAttributeAccessor(new AttributeAccessor() { Project oxProject; DBWSAdapter dbwsAdapter; @Override public Object getAttributeValueFromObject(Object object) { return ((Invocation)object).getParameters(); } @Override public void setAttributeValueInObject(Object object, Object value) { Invocation invocation = (Invocation)object; Vector values = (Vector)value; for (Iterator i = values.iterator(); i.hasNext();) { /* scan through values: * if XML conforms to something mapped, it an object; else it is a DOM Element * (probably a scalar). Walk through operations for the types, converting * as required. The 'key' is the local name of the element - for mapped objects, * have to get the element name from the schema context for the object */ Object o = i.next(); if (o instanceof Element) { Element e = (Element)o; String key = e.getLocalName(); if ("theInstance".equals(key)) { NodeList nl = e.getChildNodes(); for (int j = 0; j < nl.getLength(); j++) { Node n = nl.item(j); if (n.getNodeType() == Node.ELEMENT_NODE) { try { Object theInstance = dbwsAdapter.getXMLContext().createUnmarshaller().unmarshal(n); if (theInstance instanceof XMLRoot) { theInstance = ((XMLRoot)theInstance).getObject(); } invocation.setParameter(key, theInstance); break; } catch (XMLMarshalException xmlMarshallException) { throw new WebServiceException(xmlMarshallException); } } } } else { ClassDescriptor desc = null; for (ClassDescriptor xdesc : oxProject.getOrderedDescriptors()) { XMLSchemaReference schemaReference = xdesc instanceof XMLDescriptor ? ((XMLDescriptor)xdesc).getSchemaReference() : null; if (schemaReference != null && schemaReference.getSchemaContext().equalsIgnoreCase(key)) { desc = xdesc; break; } } if (desc != null) { try { Object theObject = dbwsAdapter.getXMLContext().createUnmarshaller().unmarshal(e, desc.getJavaClass()); if (theObject instanceof XMLRoot) { theObject = ((XMLRoot)theObject).getObject(); } invocation.setParameter(key, theObject); } catch (XMLMarshalException xmlMarshallException) { throw new WebServiceException(xmlMarshallException); } } else { String serviceName = e.getParentNode().getLocalName(); boolean found = false; for (Operation op : dbwsAdapter.getOperationsList()) { if (op.getName().equals(serviceName)) { for (Parameter p : op.getParameters()) { if (p.getName().equals(key)) { desc = dbwsAdapter.getDescriptorsByQName().get(p.getType()); if (desc != null) { found = true; } break; } } } if (found) { break; } } if (found) { Object theObject = dbwsAdapter.getXMLContext().createUnmarshaller().unmarshal(e, desc.getJavaClass()); if (theObject instanceof XMLRoot) { theObject = ((XMLRoot)theObject).getObject(); } invocation.setParameter(key, theObject); } else { // cant use e.getTextContent() - some DOM impls dont support it :-( //String val = e.getTextContent(); StringBuilder sb = new StringBuilder(); NodeList childNodes = e.getChildNodes(); for(int idx=0; idx < childNodes.getLength(); idx++ ) { if (childNodes.item(idx).getNodeType() == Node.TEXT_NODE ) { sb.append(childNodes.item(idx).getNodeValue()); } } invocation.setParameter(key, sb.toString()); } } } } else { XMLDescriptor descriptor = (XMLDescriptor)oxProject.getDescriptor(o.getClass()); String key = descriptor.getDefaultRootElement(); int idx = key.indexOf(':'); if (idx != -1) { key = key.substring(idx+1); } invocation.setParameter(key, o); } } } public AttributeAccessor setProjectAndAdapter(Project oxProject, DBWSAdapter dbwsAdapter) { this.oxProject = oxProject; this.dbwsAdapter = dbwsAdapter; return this; } }.setProjectAndAdapter(oxProject, dbwsAdapter)); parametersMapping.setKeepAsElementPolicy(KEEP_UNKNOWN_AS_ELEMENT); invocationDescriptor.addMapping(parametersMapping); oxProject.addDescriptor(invocationDescriptor); ((DatabaseSessionImpl)dbwsAdapter.getOXSession()).initializeDescriptorIfSessionAlive(invocationDescriptor); dbwsAdapter.getXMLContext().storeXMLDescriptorByQName(invocationDescriptor); // create SOAP message response handler of appropriate version responseWriter = new SOAPResponseWriter(dbwsAdapter); responseWriter.initialize(); } @SuppressWarnings({"unchecked"}) public SOAPMessage invoke(SOAPMessage request) { Map attachments = null; if (mtomEnabled) { attachments = (Map)mc.get(INBOUND_MESSAGE_ATTACHMENTS); } SOAPMessage response; boolean usesSOAP12; DBWSAdapter dbwsAdapter = (DBWSAdapter)xrService; SOAPEnvelope envelope; try { envelope = request.getSOAPPart().getEnvelope(); } catch (SOAPException se) { throw new WebServiceException(se.getMessage(), se); } // check soap 1.2 Namespace in envelope String namespaceURI = envelope.getNamespaceURI(); usesSOAP12 = namespaceURI.equals(URI_NS_SOAP_1_2_ENVELOPE); SOAPElement body; try { body = getSOAPBodyElement(envelope); } catch (SOAPException se) { throw new WebServiceException(se.getMessage(), se); } if (body == null) { SOAPFault soapFault; try { SOAPFactory soapFactory; if (usesSOAP12) { soapFactory = SOAPFactory.newInstance(SOAP_1_2_PROTOCOL); } else { soapFactory = SOAPFactory.newInstance(); } QName faultCodeQName; if (usesSOAP12) { faultCodeQName = SENDER_QNAME; } else { faultCodeQName = CLIENT_QNAME; } soapFault = soapFactory.createFault("SOAPMessage request format error - missing body element", faultCodeQName); } catch (SOAPException se) { throw new WebServiceException(se.getMessage(), se); } throw new SOAPFaultException(soapFault); } XMLRoot xmlRoot; try { XMLContext xmlContext = dbwsAdapter.getXMLContext(); XMLUnmarshaller unmarshaller = xmlContext.createUnmarshaller(); if (attachments != null && attachments.size() > 0) { unmarshaller.setAttachmentUnmarshaller(new XMLAttachmentUnmarshaller() { Map attachments; public XMLAttachmentUnmarshaller setAttachments(Map attachments) { this.attachments = attachments; return this; } @Override public boolean isXOPPackage() { return true; } @Override public DataHandler getAttachmentAsDataHandler(String id) { // strip off 'cid:' (Is this needed?) String attachmentRefId = id; if (attachmentRefId.startsWith("cid:")) { attachmentRefId = attachmentRefId.substring(4); } return attachments.get(attachmentRefId); } @Override public byte[] getAttachmentAsByteArray(String id) { ByteArrayOutputStream out = null; try { DataHandler dh = attachments.get(id); if (dh == null) { return null; } InputStream in = dh.getInputStream(); out = new ByteArrayOutputStream(1024); byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } } catch (IOException e) { // e.printStackTrace(); } if (out != null) { return out.toByteArray(); } return null; } }.setAttachments(attachments)); dbwsAdapter.setCurrentAttachmentUnmarshaller(unmarshaller.getAttachmentUnmarshaller()); } xmlRoot = (XMLRoot)unmarshaller.unmarshal(body, Invocation.class); } catch (Exception e) { SOAPFault soapFault; try { SOAPFactory soapFactory; if (usesSOAP12) { soapFactory = SOAPFactory.newInstance(SOAP_1_2_PROTOCOL); } else { soapFactory = SOAPFactory.newInstance(); } QName faultCodeQName; if (usesSOAP12) { faultCodeQName = SENDER_QNAME; } else { faultCodeQName = CLIENT_QNAME; } Throwable e1 = e; if (e.getCause() != null) { e1 = e.getCause(); } soapFault = soapFactory.createFault("SOAPMessage request format error - " + e1, faultCodeQName); } catch (SOAPException se) { throw new WebServiceException(se.getMessage(), se); } throw new SOAPFaultException(soapFault); } Invocation invocation = (Invocation)xmlRoot.getObject(); invocation.setName(xmlRoot.getLocalName()); Operation op = dbwsAdapter.getOperation(invocation.getName()); /* * Fix up types for arguments - scan the extended schema for the operation's Request type. * * For most parameters, the textual node content is fine, but for date/time and * binary objects, we must convert */ org.eclipse.persistence.internal.oxm.schema.model.Element invocationElement = dbwsAdapter.getExtendedSchema().getTopLevelElements().get(invocation.getName()); String typeName = invocationElement.getType(); int idx = typeName.indexOf(':'); if (idx != -1) { // strip-off any namespace prefix typeName = typeName.substring(idx+1); } ComplexType complexType = dbwsAdapter.getExtendedSchema().getTopLevelComplexTypes().get(typeName); if (complexType.getSequence() != null) { // for each operation, there is a corresponding top-level Request type // which has the arguments to the operation for (Iterator i = complexType.getSequence().getOrderedElements().iterator(); i .hasNext();) { org.eclipse.persistence.internal.oxm.schema.model.Element e = i.next(); String argName = e.getName(); Object argValue = invocation.getParameter(argName); String argType = e.getType(); if (argType != null) { String argTypePrefix = null; String nameSpaceURI = null; idx = argType.indexOf(':'); if (idx != -1) { argTypePrefix = argType.substring(0,idx); argType = argType.substring(idx+1); nameSpaceURI = dbwsAdapter.getSchema().getNamespaceResolver().resolveNamespacePrefix(argTypePrefix); } QName argQName = argTypePrefix == null ? new QName(nameSpaceURI, argType) : new QName(nameSpaceURI, argType, argTypePrefix); Class clz = SCHEMA_2_CLASS.get(argQName); if (clz != null) { argValue = ((XMLConversionManager)dbwsAdapter.getOXSession().getDatasourcePlatform(). getConversionManager()).convertObject(argValue, clz, argQName); invocation.setParameter(argName, argValue); } } // incoming attachments ? } } Object result; try { result = op.invoke(dbwsAdapter, invocation); if (result instanceof ValueObject) { result = ((ValueObject)result).value; } response = responseWriter.generateResponse(op, usesSOAP12, result); } catch (SOAPException se) { throw new WebServiceException(se.getMessage(), se); } catch (Exception e) { try { response = responseWriter.generateResponse(op, usesSOAP12, e); } catch (SOAPException soape1) { SOAPFault soapFault; try { SOAPFactory soapFactory; if (usesSOAP12) { soapFactory = SOAPFactory.newInstance(SOAP_1_2_PROTOCOL); } else { soapFactory = SOAPFactory.newInstance(); } QName faultCodeQName; if (usesSOAP12) { faultCodeQName = RECEIVER_QNAME; } else { faultCodeQName = SERVER_QNAME; } soapFault = soapFactory.createFault("SOAPMessage response error - " + e.getMessage(), faultCodeQName); } catch (SOAPException soape2) { throw new WebServiceException(soape2.getMessage(), soape2); } throw new SOAPFaultException(soapFault); } } return response; } public void destroy() { logoutSessions(); responseWriter = null; try { xrSchemaStream.close(); } catch (IOException ioe) { /* safe to ignore */ } xrSchemaStream = null; parentClassLoader = null; xrService.setXMLContext(null); xrService = null; } @Override public XRServiceAdapter buildService(XRServiceModel xrServiceModel) { xrService = new DBWSAdapter(); // use subclass to hold extended WSDL schema DBWSAdapter dbws = (DBWSAdapter)xrService; dbws.setName(xrServiceModel.getName()); dbws.setSessionsFile(xrServiceModel.getSessionsFile()); dbws.setOperations(xrServiceModel.getOperations()); initializeService(parentClassLoader, xrSchemaStream); return dbws; } public static SOAPElement getSOAPBodyElement(SOAPEnvelope envelope) throws SOAPException { for (@SuppressWarnings("rawtypes") Iterator it = envelope.getBody().getChildElements(); it.hasNext();) { Object node = it.next(); if (node instanceof SOAPBodyElement) { return (SOAPElement) node; } } return null; } }