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.rest.httppart;
014
015import static org.apache.juneau.common.internal.ArgUtils.*;
016import static org.apache.juneau.internal.CollectionUtils.*;
017
018import java.util.*;
019
020import jakarta.servlet.http.*;
021
022import org.apache.juneau.collections.*;
023import org.apache.juneau.rest.*;
024import org.apache.juneau.svl.*;
025
026/**
027 * Represents the attributes in an HTTP request.
028 *
029 * <p>
030 *    The {@link RequestAttributes} object is the API for accessing the standard servlet attributes on an HTTP request
031 *    (i.e. {@link jakarta.servlet.ServletRequest#getAttribute(String)}).
032 * </p>
033 *
034 * <p class='bjava'>
035 *    <ja>@RestPost</ja>(...)
036 *    <jk>public</jk> Object myMethod(RequestAttributes <jv>attributes</jv>) {...}
037 * </p>
038 *
039 * <h5 class='figure'>Example:</h5>
040 * <p class='bjava'>
041 *    <ja>@RestPost</ja>(...)
042 *    <jk>public</jk> Object myMethod(RequestAttributes <jv>attributes</jv>) {
043 *
044 *       <jc>// Add a default value.</jc>
045 *       <jv>attributes</jv>.addDefault(<js>"Foo"</js>, 123);
046 *
047 *       <jc>// Get an attribute value as a POJO.</jc>
048 *       UUID <jv>etag</jv> = <jv>attributes</jv>.get(<js>"ETag"</js>).as(UUID.<jk>class</jk>).orElse(<jk>null</jk>);
049 *    }
050 * </p>
051 *
052 * <p>
053 *    Some important methods on this class are:
054 * </p>
055 * <ul class='javatree'>
056 *    <li class='jc'>{@link RequestHeaders}
057 *    <ul class='spaced-list'>
058 *       <li>Methods for retrieving request attributes:
059 *       <ul class='javatreec'>
060 *          <li class='jm'>{@link RequestAttributes#contains(String...) contains(String...)}
061 *          <li class='jm'>{@link RequestAttributes#containsAny(String...) containsAny(String...)}
062 *          <li class='jm'>{@link RequestAttributes#get(String) get(String)}
063 *          <li class='jm'>{@link RequestAttributes#getAll() getAll()}
064 *       </ul>
065 *       <li>Methods for overriding request attributes:
066 *       <ul class='javatreec'>
067 *          <li class='jm'>{@link RequestAttributes#addDefault(List) addDefault(List)}
068 *          <li class='jm'>{@link RequestAttributes#addDefault(NamedAttribute...) addDefault(NamedAttribute...)}
069 *          <li class='jm'>{@link RequestAttributes#addDefault(NamedAttributeMap) addDefault(NamedAttributeMap)}
070 *          <li class='jm'>{@link RequestAttributes#addDefault(String,Object) addDefault(String,Object)}
071 *          <li class='jm'>{@link RequestAttributes#remove(NamedAttribute...) remove(NamedAttribute...)}
072 *          <li class='jm'>{@link RequestAttributes#remove(String...) remove(String...)}
073 *          <li class='jm'>{@link RequestAttributes#set(NamedAttribute...) set(NamedAttribute...)}
074 *          <li class='jm'>{@link RequestAttributes#set(String,Object) set(String,Object)}
075 *       </ul>
076 *       <li>Other methods:
077 *       <ul class='javatreec'>
078 *          <li class='jm'>{@link RequestAttributes#asMap() asMap()}
079 *       </ul>
080 *    </ul>
081 * </ul>
082 *
083 * <p>
084 *    Modifications made to request attributes through the <c>RequestAttributes</c> bean are automatically reflected in
085 *    the underlying servlet request attributes making it possible to mix the usage of both APIs.
086 * </p>
087 *
088 * <h5 class='section'>See Also:</h5><ul>
089 *    <li class='link'><a class="doclink" href="../../../../../index.html#jrs.HttpParts">HTTP Parts</a>
090 * </ul>
091 */
092public class RequestAttributes {
093
094   final RestRequest req;
095   final HttpServletRequest sreq;
096   final VarResolverSession vs;
097
098   /**
099    * Constructor.
100    *
101    * @param req The request creating this bean.
102    */
103   public RequestAttributes(RestRequest req) {
104      this.req = req;
105      this.sreq = req.getHttpServletRequest();
106      this.vs = req.getVarResolverSession();
107   }
108
109   /**
110    * Adds default entries to the request attributes.
111    *
112    * @param pairs
113    *    The default entries.
114    *    <br>Can be <jk>null</jk>.
115    * @return This object.
116    */
117   public RequestAttributes addDefault(List<NamedAttribute> pairs) {
118      for (NamedAttribute p : pairs)
119         if (sreq.getAttribute(p.getName()) == null) {
120            Object o = p.getValue();
121            sreq.setAttribute(p.getName(), o instanceof String ? vs.resolve(o) : o);
122         }
123      return this;
124   }
125
126   /**
127    * Adds default entries to the request attributes.
128    *
129    * @param pairs
130    *    The default entries.
131    *    <br>Can be <jk>null</jk>.
132    * @return This object.
133    */
134   public RequestAttributes addDefault(NamedAttributeMap pairs) {
135      for (NamedAttribute p : pairs.values())
136         if (sreq.getAttribute(p.getName()) == null) {
137            Object o = p.getValue();
138            sreq.setAttribute(p.getName(), o instanceof String ? vs.resolve(o) : o);
139         }
140      return this;
141   }
142
143   /**
144    * Adds default entries to the request attributes.
145    *
146    * @param pairs
147    *    The default entries.
148    *    <br>Can be <jk>null</jk>.
149    * @return This object.
150    */
151   public RequestAttributes addDefault(NamedAttribute...pairs) {
152      return addDefault(alist(pairs));
153   }
154
155   /**
156    * Adds a default entry to the request attributes.
157    *
158    * @param name The name.
159    * @param value The value.
160    * @return This object.
161    */
162   public RequestAttributes addDefault(String name, Object value) {
163      return addDefault(BasicNamedAttribute.of(name, value));
164   }
165
166   /**
167    * Returns the request attribute with the specified name.
168    *
169    * @param name The attribute name.
170    * @return The parameter value, or {@link Optional#empty()} if it doesn't exist.
171    */
172   public RequestAttribute get(String name) {
173      return new RequestAttribute(req, name, sreq.getAttribute(name));
174   }
175
176   /**
177    * Returns all the attribute on this request.
178    *
179    * @return All the attribute on this request.
180    */
181   public List<RequestAttribute> getAll() {
182      List<RequestAttribute> l = list();
183      Enumeration<String> e = sreq.getAttributeNames();
184      while (e.hasMoreElements()) {
185         String n = e.nextElement();
186         l.add(new RequestAttribute(req, n, sreq.getAttribute(n)));
187      }
188      return l;
189   }
190
191   /**
192    * Returns <jk>true</jk> if the attributes with the specified names are present.
193    *
194    * @param names The attribute names.  Must not be <jk>null</jk>.
195    * @return <jk>true</jk> if the parameters with the specified names are present.
196    */
197   public boolean contains(String...names) {
198      assertArgNotNull("names", names);
199      for (String n : names)
200         if (sreq.getAttribute(n) == null)
201            return false;
202      return true;
203   }
204
205   /**
206    * Returns <jk>true</jk> if the attribute with any of the specified names are present.
207    *
208    * @param names The attribute names.  Must not be <jk>null</jk>.
209    * @return <jk>true</jk> if the attribute with any of the specified names are present.
210    */
211   public boolean containsAny(String...names) {
212      assertArgNotNull("names", names);
213      for (String n : names)
214         if (sreq.getAttribute(n) != null)
215            return true;
216      return false;
217   }
218
219   /**
220    * Sets a request attribute.
221    *
222    * @param name The attribute name.  Must not be <jk>null</jk>.
223    * @param value
224    *    The attribute value.
225    *    <br>Can be <jk>null</jk>.
226    * @return This object.
227    */
228   public RequestAttributes set(String name, Object value) {
229      assertArgNotNull("name", name);
230      sreq.setAttribute(name, value);
231      return this;
232   }
233
234   /**
235    * Sets request attributes.
236    *
237    * @param attributes The parameters to set.  Must not be <jk>null</jk> or contain <jk>null</jk>.
238    * @return This object.
239    */
240   public RequestAttributes set(NamedAttribute...attributes) {
241      assertArgNotNull("attributes", attributes);
242      for (NamedAttribute p : attributes)
243         set(p);
244      return this;
245   }
246
247   /**
248    * Remove request attributes.
249    *
250    * @param name The attribute names.  Must not be <jk>null</jk>.
251    * @return This object.
252    */
253   public RequestAttributes remove(String...name) {
254      assertArgNotNull("name", name);
255      for (String n : name) {
256         sreq.removeAttribute(n);
257      }
258      return this;
259   }
260
261   /**
262    * Remove request attributes.
263    *
264    * @param attributes The attributes to remove.  Must not be <jk>null</jk>.
265    * @return This object.
266    */
267   public RequestAttributes remove(NamedAttribute...attributes) {
268      for (NamedAttribute p : attributes)
269         remove(p.getName());
270      return this;
271   }
272
273   /**
274    * Returns the request attributes as a map.
275    *
276    * @return The request attributes as a map.  Never <jk>null</jk>.
277    */
278   public Map<String,Object> asMap() {
279      JsonMap m = new JsonMap();
280      Enumeration<String> e = sreq.getAttributeNames();
281      while (e.hasMoreElements()) {
282         String n = e.nextElement();
283         m.put(n, sreq.getAttribute(n));
284      }
285      return m;
286   }
287
288   @Override /* Object */
289   public String toString() {
290      return asMap().toString();
291   }
292}