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      super();
105      this.req = req;
106      this.sreq = req.getHttpServletRequest();
107      this.vs = req.getVarResolverSession();
108   }
109
110   /**
111    * Adds default entries to the request attributes.
112    *
113    * @param pairs
114    *    The default entries.
115    *    <br>Can be <jk>null</jk>.
116    * @return This object.
117    */
118   public RequestAttributes addDefault(List<NamedAttribute> pairs) {
119      for (NamedAttribute p : pairs)
120         if (sreq.getAttribute(p.getName()) == null) {
121            Object o = p.getValue();
122            sreq.setAttribute(p.getName(), o instanceof String ? vs.resolve(o) : o);
123         }
124      return this;
125   }
126
127   /**
128    * Adds default entries to the request attributes.
129    *
130    * @param pairs
131    *    The default entries.
132    *    <br>Can be <jk>null</jk>.
133    * @return This object.
134    */
135   public RequestAttributes addDefault(NamedAttributeMap pairs) {
136      for (NamedAttribute p : pairs.values())
137         if (sreq.getAttribute(p.getName()) == null) {
138            Object o = p.getValue();
139            sreq.setAttribute(p.getName(), o instanceof String ? vs.resolve(o) : o);
140         }
141      return this;
142   }
143
144   /**
145    * Adds default entries to the request attributes.
146    *
147    * @param pairs
148    *    The default entries.
149    *    <br>Can be <jk>null</jk>.
150    * @return This object.
151    */
152   public RequestAttributes addDefault(NamedAttribute...pairs) {
153      return addDefault(alist(pairs));
154   }
155
156   /**
157    * Adds a default entry to the request attributes.
158    *
159    * @param name The name.
160    * @param value The value.
161    * @return This object.
162    */
163   public RequestAttributes addDefault(String name, Object value) {
164      return addDefault(BasicNamedAttribute.of(name, value));
165   }
166
167   /**
168    * Returns the request attribute with the specified name.
169    *
170    * @param name The attribute name.
171    * @return The parameter value, or {@link Optional#empty()} if it doesn't exist.
172    */
173   public RequestAttribute get(String name) {
174      return new RequestAttribute(req, name, sreq.getAttribute(name));
175   }
176
177   /**
178    * Returns all the attribute on this request.
179    *
180    * @return All the attribute on this request.
181    */
182   public List<RequestAttribute> getAll() {
183      List<RequestAttribute> l = list();
184      Enumeration<String> e = sreq.getAttributeNames();
185      while (e.hasMoreElements()) {
186         String n = e.nextElement();
187         l.add(new RequestAttribute(req, n, sreq.getAttribute(n)));
188      }
189      return l;
190   }
191
192   /**
193    * Returns <jk>true</jk> if the attributes with the specified names are present.
194    *
195    * @param names The attribute names.  Must not be <jk>null</jk>.
196    * @return <jk>true</jk> if the parameters with the specified names are present.
197    */
198   public boolean contains(String...names) {
199      assertArgNotNull("names", names);
200      for (String n : names)
201         if (sreq.getAttribute(n) == null)
202            return false;
203      return true;
204   }
205
206   /**
207    * Returns <jk>true</jk> if the attribute with any of the specified names are present.
208    *
209    * @param names The attribute names.  Must not be <jk>null</jk>.
210    * @return <jk>true</jk> if the attribute with any of the specified names are present.
211    */
212   public boolean containsAny(String...names) {
213      assertArgNotNull("names", names);
214      for (String n : names)
215         if (sreq.getAttribute(n) != null)
216            return true;
217      return false;
218   }
219
220   /**
221    * Sets a request attribute.
222    *
223    * @param name The attribute name.  Must not be <jk>null</jk>.
224    * @param value
225    *    The attribute value.
226    *    <br>Can be <jk>null</jk>.
227    * @return This object.
228    */
229   public RequestAttributes set(String name, Object value) {
230      assertArgNotNull("name", name);
231      sreq.setAttribute(name, value);
232      return this;
233   }
234
235   /**
236    * Sets request attributes.
237    *
238    * @param attributes The parameters to set.  Must not be <jk>null</jk> or contain <jk>null</jk>.
239    * @return This object.
240    */
241   public RequestAttributes set(NamedAttribute...attributes) {
242      assertArgNotNull("attributes", attributes);
243      for (NamedAttribute p : attributes)
244         set(p);
245      return this;
246   }
247
248   /**
249    * Remove request attributes.
250    *
251    * @param name The attribute names.  Must not be <jk>null</jk>.
252    * @return This object.
253    */
254   public RequestAttributes remove(String...name) {
255      assertArgNotNull("name", name);
256      for (String n : name) {
257         sreq.removeAttribute(n);
258      }
259      return this;
260   }
261
262   /**
263    * Remove request attributes.
264    *
265    * @param attributes The attributes to remove.  Must not be <jk>null</jk>.
266    * @return This object.
267    */
268   public RequestAttributes remove(NamedAttribute...attributes) {
269      for (NamedAttribute p : attributes)
270         remove(p.getName());
271      return this;
272   }
273
274   /**
275    * Returns the request attributes as a map.
276    *
277    * @return The request attributes as a map.  Never <jk>null</jk>.
278    */
279   public Map<String,Object> asMap() {
280      JsonMap m = new JsonMap();
281      Enumeration<String> e = sreq.getAttributeNames();
282      while (e.hasMoreElements()) {
283         String n = e.nextElement();
284         m.put(n, sreq.getAttribute(n));
285      }
286      return m;
287   }
288
289   @Override /* Object */
290   public String toString() {
291      return asMap().toString();
292   }
293}