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