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