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.arg; 014 015import static org.apache.juneau.internal.CollectionUtils.*; 016import static org.apache.juneau.common.internal.StringUtils.*; 017import static org.apache.juneau.http.annotation.HeaderAnnotation.*; 018 019import java.util.*; 020 021import org.apache.juneau.*; 022import org.apache.juneau.annotation.*; 023import org.apache.juneau.collections.*; 024import org.apache.juneau.http.annotation.*; 025import org.apache.juneau.http.header.*; 026import org.apache.juneau.httppart.*; 027import org.apache.juneau.internal.*; 028import org.apache.juneau.reflect.*; 029import org.apache.juneau.rest.*; 030import org.apache.juneau.rest.annotation.*; 031import org.apache.juneau.rest.httppart.*; 032 033/** 034 * Resolves method parameters and parameter types annotated with {@link Header} on {@link RestOp}-annotated Java methods. 035 * 036 * <p> 037 * This includes any of the following predefined request header types: 038 * 039 * <ul class='javatree condensed'> 040 * <li class='jc'>{@link Accept} 041 * <li class='jc'>{@link AcceptCharset} 042 * <li class='jc'>{@link AcceptEncoding} 043 * <li class='jc'>{@link AcceptLanguage} 044 * <li class='jc'>{@link AcceptRanges} 045 * <li class='jc'>{@link Authorization} 046 * <li class='jc'>{@link CacheControl} 047 * <li class='jc'>{@link ClientVersion} 048 * <li class='jc'>{@link Connection} 049 * <li class='jc'>{@link ContentDisposition} 050 * <li class='jc'>{@link ContentEncoding} 051 * <li class='jc'>{@link ContentLength} 052 * <li class='jc'>{@link ContentType} 053 * <li class='jc'>{@link org.apache.juneau.http.header.Date} 054 * <li class='jc'>{@link Debug} 055 * <li class='jc'>{@link Expect} 056 * <li class='jc'>{@link Forwarded} 057 * <li class='jc'>{@link From} 058 * <li class='jc'>{@link Host} 059 * <li class='jc'>{@link IfMatch} 060 * <li class='jc'>{@link IfModifiedSince} 061 * <li class='jc'>{@link IfNoneMatch} 062 * <li class='jc'>{@link IfRange} 063 * <li class='jc'>{@link IfUnmodifiedSince} 064 * <li class='jc'>{@link MaxForwards} 065 * <li class='jc'>{@link NoTrace} 066 * <li class='jc'>{@link Origin} 067 * <li class='jc'>{@link Pragma} 068 * <li class='jc'>{@link ProxyAuthorization} 069 * <li class='jc'>{@link Range} 070 * <li class='jc'>{@link Referer} 071 * <li class='jc'>{@link TE} 072 * <li class='jc'>{@link Thrown} 073 * <li class='jc'>{@link Upgrade} 074 * <li class='jc'>{@link UserAgent} 075 * <li class='jc'>{@link Warning} 076 * </ul> 077 * 078 * <p> 079 * The parameter value is resolved using: 080 * <p class='bjava'> 081 * <jv>opSession</jv> 082 * .{@link RestOpSession#getRequest() getRequest}() 083 * .{@link RestRequest#getHeaders() getHeaders}(); 084 * </p> 085 * 086 * <p> 087 * {@link HttpPartSchema schema} is derived from the {@link Header} annotation. 088 * 089 * <p> 090 * If the {@link Schema#collectionFormat()} value is {@link HttpPartCollectionFormat#MULTI}, then the data type can be a {@link Collection} or array. 091 * 092 * <h5 class='section'>See Also:</h5><ul> 093 * <li class='link'><a class="doclink" href="../../../../../index.html#jrs.JavaMethodParameters">Java Method Parameters</a> 094 * </ul> 095 */ 096public class HeaderArg implements RestOpArg { 097 private final HttpPartParser partParser; 098 private final HttpPartSchema schema; 099 private final boolean multi; 100 private final String name, def; 101 private final ClassInfo type; 102 103 /** 104 * Static creator. 105 * 106 * @param paramInfo The Java method parameter being resolved. 107 * @param annotations The annotations to apply to any new part parsers. 108 * @return A new {@link HeaderArg}, or <jk>null</jk> if the parameter is not annotated with {@link Header}. 109 */ 110 public static HeaderArg create(ParamInfo paramInfo, AnnotationWorkList annotations) { 111 if ((!paramInfo.getParameterType().is(Value.class)) && (paramInfo.hasAnnotation(Header.class) || paramInfo.getParameterType().hasAnnotation(Header.class))) 112 return new HeaderArg(paramInfo, annotations); 113 return null; 114 } 115 116 /** 117 * Constructor. 118 * 119 * @param pi The Java method parameter being resolved. 120 * @param annotations The annotations to apply to any new part parsers. 121 */ 122 protected HeaderArg(ParamInfo pi, AnnotationWorkList annotations) { 123 this.name = findName(pi).orElseThrow(() -> new ArgException(pi, "@Header used without name or value")); 124 this.def = findDef(pi).orElse(null); 125 this.type = pi.getParameterType(); 126 this.schema = HttpPartSchema.create(Header.class, pi); 127 Class<? extends HttpPartParser> pp = schema.getParser(); 128 this.partParser = pp != null ? HttpPartParser.creator().type(pp).apply(annotations).create() : null; 129 this.multi = schema.getCollectionFormat() == HttpPartCollectionFormat.MULTI; 130 131 if (multi && ! type.isCollectionOrArray()) 132 throw new ArgException(pi, "Use of multipart flag on @Header parameter that is not an array or Collection"); 133 } 134 135 @SuppressWarnings({ "rawtypes", "unchecked" }) 136 @Override /* RestOpArg */ 137 public Object resolve(RestOpSession opSession) throws Exception { 138 RestRequest req = opSession.getRequest(); 139 HttpPartParserSession ps = partParser == null ? req.getPartParserSession() : partParser.getPartSession(); 140 RequestHeaders rh = req.getHeaders(); 141 BeanSession bs = req.getBeanSession(); 142 ClassMeta<?> cm = bs.getClassMeta(type.innerType()); 143 144 if (multi) { 145 Collection c = cm.isArray() ? list() : (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new JsonList()); 146 rh.stream(name).map(x -> x.parser(ps).schema(schema).as(cm.getElementType()).orElse(null)).forEach(x -> c.add(x)); 147 return cm.isArray() ? ArrayUtils.toArray(c, cm.getElementType().getInnerClass()) : c; 148 } 149 150 if (cm.isMapOrBean() && isOneOf(name, "*", "")) { 151 JsonMap m = new JsonMap(); 152 rh.forEach(x -> m.put(x.getName(), x.parser(ps).schema(schema == null ? null : schema.getProperty(x.getName())).as(cm.getValueType()).orElse(null))); 153 return req.getBeanSession().convertToType(m, cm); 154 } 155 156 return rh.getLast(name).parser(ps).schema(schema).def(def).as(type.innerType()).orElse(null); 157 } 158}