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<?>)} 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<?>)} 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}