001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.http;
014
015import static org.apache.juneau.internal.StringUtils.*;
016
017import java.util.*;
018import java.util.concurrent.*;
019import java.util.function.*;
020import java.util.stream.*;
021
022import org.apache.http.*;
023import org.apache.juneau.*;
024import org.apache.juneau.httppart.*;
025import org.apache.juneau.internal.*;
026import org.apache.juneau.marshall.*;
027import org.apache.juneau.oapi.*;
028import org.apache.juneau.svl.*;
029
030/**
031 * Specifies a dynamic supplier of {@link Header} objects.
032 *
033 * This class is thread safe.
034 */
035public class HeaderSupplier implements Iterable<Header> {
036
037   /** Represents no header supplier */
038   public static final class Null extends HeaderSupplier {}
039
040   private final List<Iterable<Header>> headers = new CopyOnWriteArrayList<>();
041
042   private volatile VarResolver varResolver;
043
044   /**
045    * Convenience creator.
046    *
047    * @return A new {@link HeaderSupplier} object.
048    */
049   public static HeaderSupplier create() {
050      return new HeaderSupplier();
051   }
052
053   /**
054    * Creates an empty instance.
055    *
056    * @return A new empty instance.
057    */
058   public static HeaderSupplier of() {
059      return new HeaderSupplier();
060   }
061
062   /**
063    * Creates an instance initialized with the specified headers.
064    *
065    * @param headers The headers to add to this list.
066    * @return A new instance.
067    */
068   public static HeaderSupplier of(Collection<Header> headers) {
069      return new HeaderSupplier().addAll(headers);
070   }
071
072   /**
073    * Creates an instance initialized with the specified name/value pairs.
074    *
075    * @param parameters
076    *    Initial list of parameters.
077    *    <br>Must be an even number of parameters representing key/value pairs.
078    * @throws RuntimeException If odd number of parameters were specified.
079    * @return A new instance.
080    */
081   public static HeaderSupplier ofPairs(Object...parameters) {
082      HeaderSupplier s = HeaderSupplier.create();
083      if (parameters.length % 2 != 0)
084         throw new BasicRuntimeException("Odd number of parameters passed into HeaderSupplier.ofPairs()");
085      for (int i = 0; i < parameters.length; i+=2)
086         s.add(stringify(parameters[i]), parameters[i+1]);
087      return s;
088   }
089
090   /**
091    * Convenience creator.
092    *
093    * @param values
094    *    The values to populate this supplier with.
095    *    <br>Can be any of the following types:
096    *    <ul>
097    *       <li>{@link Header}.
098    *       <li>{@link HeaderSupplier}.
099    *    </ul>
100    * @return A new {@link HeaderSupplier} object.
101    */
102   public static HeaderSupplier of(Object...values) {
103      HeaderSupplier s = HeaderSupplier.create();
104      for (Object v : values) {
105         if (v instanceof Header)
106            s.add((Header)v);
107         else if (v instanceof HeaderSupplier)
108            s.add((HeaderSupplier)v);
109         else if (v != null)
110            throw new BasicRuntimeException("Invalid type passed to HeaderSupplier.of(): {0}", v.getClass().getName());
111      }
112      return s;
113   }
114
115   /**
116    * Allows header values to contain SVL variables.
117    *
118    * <p>
119    * Resolves variables in header values when using the following methods:
120    * <ul>
121    *    <li class='jm'>{@link #ofPairs(Object...) ofPairs(Object...)}
122    *    <li class='jm'>{@link #add(String, Object) add(String,Object)}
123    *    <li class='jm'>{@link #add(String, Supplier) add(String,Supplier&lt;?&gt;)}
124    *    <li class='jm'>{@link #add(String, Object, HttpPartSerializerSession, HttpPartSchema, boolean) add(String,Object,HttpPartSerializerSession,HttpPartSchema,boolean)}
125    * </ul>
126    *
127    * <p>
128    * Uses {@link VarResolver#DEFAULT} to resolve variables.
129    *
130    * @return This object (for method chaining).
131    */
132   public HeaderSupplier resolving() {
133      return resolving(VarResolver.DEFAULT);
134   }
135
136   /**
137    * Allows header values to contain SVL variables.
138    *
139    * <p>
140    * Resolves variables in header values when using the following methods:
141    * <ul>
142    *    <li class='jm'>{@link #ofPairs(Object...) ofPairs(Object...)}
143    *    <li class='jm'>{@link #add(String, Object) add(String,Object)}
144    *    <li class='jm'>{@link #add(String, Supplier) add(String,Supplier&lt;?&gt;)}
145    *    <li class='jm'>{@link #add(String, Object, HttpPartSerializerSession, HttpPartSchema, boolean) add(String,Object,HttpPartSerializerSession,HttpPartSchema,boolean)}
146    * </ul>
147    *
148    * @param varResolver The variable resolver to use for resolving variables.
149    * @return This object (for method chaining).
150    */
151   public HeaderSupplier resolving(VarResolver varResolver) {
152      this.varResolver = varResolver;
153      return this;
154   }
155
156   /**
157    * Add a header to this supplier.
158    *
159    * @param h The header to add. <jk>null</jk> values are ignored.
160    * @return This object (for method chaining).
161    */
162   public HeaderSupplier add(Header h) {
163      if (h != null)
164         headers.add(Collections.singleton(h));
165      return this;
166   }
167
168   /**
169    * Add a supplier to this supplier.
170    *
171    * @param h The supplier to add. <jk>null</jk> values are ignored.
172    * @return This object (for method chaining).
173    */
174   public HeaderSupplier add(HeaderSupplier h) {
175      if (h != null)
176         headers.add(h);
177      return this;
178   }
179
180   /**
181    * Adds all the specified headers to this supplier.
182    *
183    * @param headers The headers to add to this supplier.
184    * @return This object(for method chaining).
185    */
186   private HeaderSupplier addAll(Collection<Header> headers) {
187      this.headers.addAll(headers.stream().filter(x->x != null).map(x->Collections.singleton(x)).collect(Collectors.toList()));
188      return this;
189   }
190
191   //------------------------------------------------------------------------------------------------------------------
192   // Appenders
193   //------------------------------------------------------------------------------------------------------------------
194
195   /**
196    * Appends the specified header to the end of this list.
197    *
198    * <p>
199    * The header is added as a {@link BasicHeader}.
200    *
201    * @param name The header name.
202    * @param value The header value.
203    * @return This object (for method chaining).
204    */
205   public HeaderSupplier add(String name, Object value) {
206      return add(new BasicHeader(name, resolver(value)));
207   }
208
209   /**
210    * Appends the specifiedheader to the end of this list using a value supplier.
211    *
212    * <p>
213    * The header is added as a {@link BasicHeader}.
214    *
215    * <p>
216    * Value is re-evaluated on each call to {@link BasicHeader#getValue()}.
217    *
218    * @param name The header name.
219    * @param value The header value supplier.
220    * @return This object (for method chaining).
221    */
222   public HeaderSupplier add(String name, Supplier<?> value) {
223      return add(new BasicHeader(name, resolver(value)));
224   }
225
226   /**
227    * Appends the specified header to the end of this list.
228    *
229    * @param name The header name.
230    * @param value The header value.
231    * @param serializer
232    *    The serializer to use for serializing the value to a string value.
233    * @param schema
234    *    The schema object that defines the format of the output.
235    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
236    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
237    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
238    * @param skipIfEmpty If value is a blank string, the value should return as <jk>null</jk>.
239    * @return This object (for method chaining).
240    */
241   public HeaderSupplier add(String name, Object value, HttpPartSerializerSession serializer, HttpPartSchema schema, boolean skipIfEmpty) {
242      return add(new SerializedHeader(name, resolver(value), serializer, schema, skipIfEmpty));
243   }
244
245   /**
246    * Returns this list as a JSON list of strings.
247    */
248   @Override /* Object */
249   public String toString() {
250      return SimpleJson.DEFAULT.toString(toArray());
251   }
252
253   @Override
254   public Iterator<Header> iterator() {
255      return CollectionUtils.iterator(headers);
256   }
257
258   /**
259    * Returns these headers as an array.
260    *
261    * @return These headers as an array.
262    */
263   public Header[] toArray() {
264      ArrayList<Header> l = new ArrayList<>();
265      for (Header p : this)
266         l.add(p);
267      return l.toArray(new Header[l.size()]);
268   }
269
270   /**
271    * Returns these headers as an array.
272    *
273    * @param array The array to copy in to.
274    * @return These headers as an array.
275    */
276   public <T extends Header> T[] toArray(T[] array) {
277      ArrayList<Header> l = new ArrayList<>();
278      for (Header p : this)
279         l.add(p);
280      return l.toArray(array);
281   }
282
283   private Supplier<Object> resolver(Object input) {
284      return ()->(varResolver == null ? unwrap(input) : varResolver.resolve(stringify(unwrap(input))));
285   }
286
287   private Object unwrap(Object o) {
288      while (o instanceof Supplier)
289         o = ((Supplier<?>)o).get();
290      return o;
291   }
292}