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

feign.soap.SOAPEncoder Maven / Gradle / Ivy

There is a newer version: 13.4
Show newest version
/*
 * Copyright 2012-2023 The Feign Authors
 *
 * Licensed 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 feign.soap;

import feign.RequestTemplate;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import feign.jaxb.JAXBContextFactory;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.soap.MessageFactory;
import jakarta.xml.soap.SOAPConstants;
import jakarta.xml.soap.SOAPException;
import jakarta.xml.soap.SOAPMessage;
import org.w3c.dom.Document;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

/**
 * Encodes requests using SOAPMessage and JAXB for the body part. 
* *

* Basic example with Feign.Builder: * *

 *
 * public interface MyApi {
 *
 *    @RequestLine("POST /getObject")
 *    @Headers({
 *      "SOAPAction: getObject",
 *      "Content-Type: text/xml"
 *    })
 *    MyJaxbObjectResponse getObject(MyJaxbObjectRequest request);
 *
 * }
 *
 * ...
 *
 * JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder()
 *     .withMarshallerJAXBEncoding("UTF-8")
 *     .withMarshallerSchemaLocation("http://apihost http://apihost/schema.xsd")
 *     .build();
 *
 * api = Feign.builder()
 *     .encoder(new SOAPEncoder(jaxbFactory))
 *     .target(MyApi.class, "http://api");
 *
 * ...
 *
 * try {
 *    api.getObject(new MyJaxbObjectRequest());
 * } catch (SOAPFaultException faultException) {
 *    log.info(faultException.getFault().getFaultString());
 * }
 * 
* *

* The JAXBContextFactory should be reused across requests as it caches the created JAXB contexts. */ public class SOAPEncoder implements Encoder { private static final String DEFAULT_SOAP_PROTOCOL = SOAPConstants.SOAP_1_1_PROTOCOL; private final boolean writeXmlDeclaration; private final boolean formattedOutput; private final Charset charsetEncoding; private final JAXBContextFactory jaxbContextFactory; private final String soapProtocol; public SOAPEncoder(Builder builder) { this.jaxbContextFactory = builder.jaxbContextFactory; this.writeXmlDeclaration = builder.writeXmlDeclaration; this.charsetEncoding = builder.charsetEncoding; this.soapProtocol = builder.soapProtocol; this.formattedOutput = builder.formattedOutput; } public SOAPEncoder(JAXBContextFactory jaxbContextFactory) { this.jaxbContextFactory = jaxbContextFactory; this.writeXmlDeclaration = true; this.formattedOutput = false; this.charsetEncoding = StandardCharsets.UTF_8; this.soapProtocol = DEFAULT_SOAP_PROTOCOL; } @Override public void encode(Object object, Type bodyType, RequestTemplate template) { if (!(bodyType instanceof Class)) { throw new UnsupportedOperationException( "SOAP only supports encoding raw types. Found " + bodyType); } try { Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); Marshaller marshaller = jaxbContextFactory.createMarshaller((Class) bodyType); marshaller.marshal(object, document); SOAPMessage soapMessage = MessageFactory.newInstance(soapProtocol).createMessage(); soapMessage.setProperty( SOAPMessage.WRITE_XML_DECLARATION, Boolean.toString(writeXmlDeclaration)); soapMessage.setProperty( SOAPMessage.CHARACTER_SET_ENCODING, charsetEncoding.displayName()); soapMessage.getSOAPBody().addDocument(document); soapMessage = modifySOAPMessage(soapMessage); ByteArrayOutputStream bos = new ByteArrayOutputStream(); if (formattedOutput) { Transformer t = TransformerFactory.newInstance().newTransformer(); t.setOutputProperty(OutputKeys.INDENT, "yes"); t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); t.transform(new DOMSource(soapMessage.getSOAPPart()), new StreamResult(bos)); } else { soapMessage.writeTo(bos); } template.body(bos.toString()); } catch (SOAPException | JAXBException | ParserConfigurationException | IOException | TransformerFactoryConfigurationError | TransformerException e) { throw new EncodeException(e.toString(), e); } } /** * Override this in order to modify the SOAP message object before it's finally encoded.
* This might be useful to add SOAP Headers, which are not supported by this SOAPEncoder directly. *
* This is an example of how to add a security header: * protected SOAPMessage modifySOAPMessage(SOAPMessage soapMessage) throws SOAPException { * SOAPFactory soapFactory = SOAPFactory.newInstance(); * String uri = "http://schemas.xmlsoap.org/ws/2002/12/secext"; * String prefix = "wss"; * SOAPElement security = soapFactory.createElement("Security", prefix, uri); * SOAPElement usernameToken = soapFactory.createElement("UsernameToken", prefix, uri); * usernameToken.addChildElement("Username", prefix, uri).setValue("test"); * usernameToken.addChildElement("Password", prefix, uri).setValue("test"); * security.addChildElement(usernameToken); * soapMessage.getSOAPHeader().addChildElement(security); * return soapMessage; * } * */ protected SOAPMessage modifySOAPMessage(SOAPMessage soapMessage) throws SOAPException { // Intentionally blank return soapMessage; } /** Creates instances of {@link SOAPEncoder}. */ public static class Builder { public boolean formattedOutput = false; private JAXBContextFactory jaxbContextFactory; private boolean writeXmlDeclaration = true; private Charset charsetEncoding = StandardCharsets.UTF_8; private String soapProtocol = DEFAULT_SOAP_PROTOCOL; /** The {@link JAXBContextFactory} for body part. */ public Builder withJAXBContextFactory(JAXBContextFactory jaxbContextFactory) { this.jaxbContextFactory = jaxbContextFactory; return this; } /** Output format indent if true. Default is false */ public Builder withFormattedOutput(boolean formattedOutput) { this.formattedOutput = formattedOutput; return this; } /** Write the xml declaration if true. Default is true */ public Builder withWriteXmlDeclaration(boolean writeXmlDeclaration) { this.writeXmlDeclaration = writeXmlDeclaration; return this; } /** Specify the charset encoding. Default is {@link Charset#defaultCharset()}. */ public Builder withCharsetEncoding(Charset charsetEncoding) { this.charsetEncoding = charsetEncoding; return this; } /** * The protocol used to create message factory. Default is "SOAP 1.1 Protocol". * * @param soapProtocol a string constant representing the MessageFactory protocol. * @see SOAPConstants#SOAP_1_1_PROTOCOL * @see SOAPConstants#SOAP_1_2_PROTOCOL * @see SOAPConstants#DYNAMIC_SOAP_PROTOCOL * @see MessageFactory#newInstance(String) */ public Builder withSOAPProtocol(String soapProtocol) { this.soapProtocol = soapProtocol; return this; } public SOAPEncoder build() { if (jaxbContextFactory == null) { throw new IllegalStateException("JAXBContextFactory must be non-null"); } return new SOAPEncoder(this); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy