001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.marshall.soap;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.util.*;
022import java.util.concurrent.*;
023
024import org.apache.juneau.commons.bean.*;
025import org.apache.juneau.commons.collections.*;
026import org.apache.juneau.marshall.*;
027import org.apache.juneau.marshall.xml.*;
028
029/**
030 * Serializes POJOs to HTTP responses as XML+SOAP.
031 *
032 * <h5 class='topic'>Media types</h5>
033 *
034 * Handles <c>Accept</c> types:  <bc>text/xml+soap</bc>
035 * <p>
036 * Produces <c>Content-Type</c> types:  <bc>text/xml+soap</bc>
037 *
038 * <h5 class='topic'>Description</h5>
039 *
040 * Essentially the same output as {@link XmlDocSerializer}, except wrapped in a standard SOAP envelope.
041 *
042 * <h5 class='section'>Notes:</h5><ul>
043 *    <li class='note'>This class is thread safe and reusable.
044 * </ul>
045 *
046 */
047@SuppressWarnings({
048   "java:S110", // Inheritance depth acceptable for this class hierarchy
049   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
050   "java:S9149" // Per-format static factories intentionally shadow the parent's.
051})
052public class SoapXmlSerializer extends XmlSerializer implements SoapXmlMetaProvider {
053
054   /**
055    * Builder class.
056    */
057   public static class Builder extends XmlSerializer.Builder<Builder> {
058
059      private static final Cache<HashKey,SoapXmlSerializer> CACHE = Cache.of(HashKey.class, SoapXmlSerializer.class).build();
060
061      private String soapAction;
062
063      /**
064       * Constructor, default settings.
065       */
066      protected Builder() {
067         produces("text/xml");
068         accept("text/xml+soap");
069         soapAction = "http://www.w3.org/2003/05/soap-envelope";
070      }
071
072      /**
073       * Copy constructor.
074       *
075       * @param copyFrom The builder to copy from.
076       *    <br>Cannot be <jk>null</jk>.
077       */
078      protected Builder(Builder copyFrom) {
079         super(reqnn("copyFrom", copyFrom));
080         soapAction = copyFrom.soapAction;
081      }
082
083      /**
084       * Copy constructor.
085       *
086       * @param copyFrom The bean to copy from.
087       *    <br>Cannot be <jk>null</jk>.
088       */
089      protected Builder(SoapXmlSerializer copyFrom) {
090         super(reqnn("copyFrom", copyFrom));
091         soapAction = copyFrom.soapAction;
092      }
093
094      @Override /* Overridden from Context.Builder<?> */
095      public SoapXmlSerializer build() {
096         return cache(CACHE).build(SoapXmlSerializer.class);
097      }
098
099      @Override /* Overridden from Context.Builder<?> */
100      public Builder copy() {
101         return new Builder(this);
102      }
103
104      @Override /* Overridden from Context.Builder<?> */
105      public HashKey hashKey() {
106         // @formatter:off
107         return HashKey.of(
108            super.hashKey(),
109            soapAction
110         );
111         // @formatter:on
112      }
113
114      /**
115       * The <c>SOAPAction</c> HTTP header value to set on responses.
116       *
117       * @param value
118       *    The new value for this property.
119       *    <br>The default is <js>"http://www.w3.org/2003/05/soap-envelope"</js>.
120       *    <br>Cannot be <jk>null</jk>.
121       * @return This object.
122       */
123      public Builder soapAction(String value) {
124         soapAction = reqnn("value", value);
125         return this;
126      }
127
128
129   }
130
131   /**
132    * Creates a new builder for this object.
133    *
134    * @return A new builder.
135    */
136   public static Builder create() {
137      return new Builder();
138   }
139
140   final String soapAction;
141
142   private final Map<ClassMeta<?>,SoapXmlClassMeta> soapXmlClassMetas = new ConcurrentHashMap<>();
143   private final Map<BeanPropertyMeta,SoapXmlBeanPropertyMeta> soapXmlBeanPropertyMetas = new ConcurrentHashMap<>();
144
145   /**
146    * Constructor.
147    *
148    * @param builder The builder for this object.
149    */
150   public SoapXmlSerializer(Builder builder) {
151      super(builder);
152      soapAction = builder.soapAction;
153   }
154
155   @Override /* Overridden from Context */
156   public Builder copy() {
157      return new Builder(this);
158   }
159
160   @Override /* Overridden from Context */
161   public SoapXmlSerializerSession.Builder createSession() {
162      return SoapXmlSerializerSession.create(this);
163   }
164
165   @Override /* Overridden from Context */
166   public SoapXmlSerializerSession getSession() { return createSession().build(); }
167
168   /**
169    * The SOAPAction HTTP header value to set on responses.
170    *
171    * @see Builder#soapAction(String)
172    * @return
173    *    The SOAPAction HTTP header value to set on responses.
174    */
175   public String getSoapAction() { return soapAction; }
176
177   @Override /* Overridden from SoapXmlMetaProvider */
178   public SoapXmlBeanPropertyMeta getSoapXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
179      if (bpm == null)
180         return SoapXmlBeanPropertyMeta.DEFAULT;
181      return soapXmlBeanPropertyMetas.computeIfAbsent(bpm, k -> new SoapXmlBeanPropertyMeta(k.getDelegateFor(), this));
182   }
183
184   @Override /* Overridden from SoapXmlMetaProvider */
185   public SoapXmlClassMeta getSoapXmlClassMeta(ClassMeta<?> cm) {
186      return soapXmlClassMetas.computeIfAbsent(cm, k -> new SoapXmlClassMeta(k, this));
187   }
188
189   @Override /* Overridden from XmlSerializer */
190   protected FluentMap<String,Object> properties() {
191      return super.properties()
192         .a("soapAction", soapAction);
193   }
194}