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.arg;
018
019import static org.apache.juneau.commons.utils.Utils.*;
020import static org.apache.juneau.http.annotation.HeaderAnnotation.*;
021
022import java.lang.reflect.*;
023
024import org.apache.juneau.*;
025import org.apache.juneau.http.annotation.*;
026import org.apache.juneau.http.header.*;
027import org.apache.juneau.httppart.*;
028import org.apache.juneau.commons.lang.*;
029import org.apache.juneau.commons.reflect.*;
030import org.apache.juneau.rest.*;
031import org.apache.juneau.rest.annotation.*;
032import org.apache.juneau.rest.httppart.*;
033
034/**
035 * Resolves method parameters annotated with {@link Header} of type {@link Value} representing response headers
036 * on {@link RestOp}-annotated Java methods.
037 *
038 * <p>
039 * The parameter value must be of type {@link Value} that accepts a value that is then set via:
040 * <p class='bjava'>
041 *    <jv>opSession</jv>
042 *       .{@link RestOpSession#getResponse() getResponse}()
043 *       .{@link RestResponse#setHeader(String,String) setOutput}(<jv>name</jv>,<jv>value</jv>);
044 * </p>
045 *
046 * <h5 class='section'>See Also:</h5><ul>
047 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JavaMethodParameters">Java Method Parameters</a>
048 * </ul>
049 */
050public class ResponseHeaderArg implements RestOpArg {
051
052   private static final AnnotationProvider AP = AnnotationProvider.INSTANCE;
053
054   /**
055    * Static creator.
056    *
057    * @param paramInfo The Java method parameter being resolved.
058    * @param annotations The annotations to apply to any new part parsers.
059    * @return A new {@link ResponseHeaderArg}, or <jk>null</jk> if the parameter is not annotated with {@link Header}.
060    */
061   public static ResponseHeaderArg create(ParameterInfo paramInfo, AnnotationWorkList annotations) {
062      if (paramInfo.getParameterType().is(Value.class) && AP.has(Header.class, paramInfo))
063         return new ResponseHeaderArg(paramInfo, annotations);
064      return null;
065   }
066
067   final ResponsePartMeta meta;
068   final String name;
069
070   private final Type type;
071
072   /**
073    * Constructor.
074    *
075    * @param pi The Java method parameter being resolved.
076    * @param annotations The annotations to apply to any new part parsers.
077    */
078   protected ResponseHeaderArg(ParameterInfo pi, AnnotationWorkList annotations) {
079      this.name = findName(pi).orElseThrow(() -> new ArgException(pi, "@Header used without name or value"));
080      this.type = pi.getParameterType().innerType();
081      var schema = HttpPartSchema.create(Header.class, pi);
082
083      var ps = schema.getSerializer();
084      this.meta = new ResponsePartMeta(HttpPartType.HEADER, schema, nn(ps) ? HttpPartSerializer.creator().type(ps).apply(annotations).create() : null);
085
086      var c = type instanceof Class ? (Class<?>)type : type instanceof ParameterizedType ? (Class<?>)((ParameterizedType)type).getRawType() : null;
087      if (c != Value.class)
088         throw new ArgException(pi, "Type must be Value<?> on parameter annotated with @Header annotation");
089   }
090
091   @Override /* Overridden from RestOpArg */
092   public Object resolve(RestOpSession opSession) throws Exception {
093      var v = new Value<>();
094      v.listener(o -> {
095         var req = opSession.getRequest();
096         var res = opSession.getResponse();
097         var rpm = req.getOpContext().getResponseHeaderMeta(o);
098         if (rpm == null)
099            rpm = ResponseHeaderArg.this.meta;
100         var pss = rpm.getSerializer() == null ? req.getPartSerializerSession() : rpm.getSerializer().getPartSession();
101         res.setHeader(new SerializedHeader(name, o, pss, rpm.getSchema(), false));
102      });
103      return v;
104   }
105}