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.processor;
018
019import static org.apache.juneau.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.Utils.*;
021
022import java.io.*;
023import java.util.*;
024
025import org.apache.http.*;
026import org.apache.http.Header;
027import org.apache.juneau.http.annotation.*;
028import org.apache.juneau.http.header.*;
029import org.apache.juneau.http.response.*;
030import org.apache.juneau.rest.*;
031
032/**
033 * Response handler for {@link Response @Response}-annotated objects.
034 *
035 * <h5 class='section'>See Also:</h5><ul>
036 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ResponseProcessors">Response Processors</a>
037 * </ul>
038 */
039@SuppressWarnings("resource")
040public class ResponseBeanProcessor implements ResponseProcessor {
041
042   @Override /* Overridden from ResponseProcessor */
043   public int process(RestOpSession opSession) throws IOException {
044
045      var req = opSession.getRequest();
046      var res = opSession.getResponse();
047      var defaultPartSerializer = req.getOpContext().getPartSerializer();
048
049      var output = res.getContent(Object.class);
050
051      if (output == null || ! (nn(output.getClass().getAnnotation(Response.class)) || nn(res.getResponseBeanMeta())))
052         return NEXT;
053
054      var rm = res.getResponseBeanMeta();
055      if (rm == null)
056         rm = req.getOpContext().getResponseBeanMeta(output);
057
058      var stm = rm.getStatusMethod();
059      if (nn(stm)) {
060         try {
061            res.setStatus((int)stm.getGetter().invoke(output));
062         } catch (Exception e) {
063            throw new InternalServerError(e, "Could not get status.");
064         }
065      } else if (rm.getCode() != 0) {
066         res.setStatus(rm.getCode());
067      }
068
069      for (var hm : rm.getHeaderMethods()) {
070         var n = hm.getPartName().orElse(null);
071         try {
072            var o = hm.getGetter().invoke(output);
073            var ps = hm.getSchema();
074            if ("*".equals(n)) {
075               for (var o2 : iterate(o)) {
076                  var h = (Header)null;
077                  if (o2 instanceof Map.Entry o22) {
078                     var x = o22;
079                     var k = s(x.getKey());
080                     h = new SerializedHeader(k, x.getValue(), hm.getSerializer().orElse(defaultPartSerializer).getPartSession(), ps.getProperty(k), true);
081                  } else if (o2 instanceof Header o22) {
082                     h = o22;
083                  } else if (o2 instanceof NameValuePair o23) {
084                     h = BasicHeader.of(o23);
085                  } else {
086                     throw new InternalServerError("Invalid type ''{0}'' for header ''{1}''", cn(o2), n);
087                  }
088                  res.addHeader(h);
089               }
090            } else {
091               var h = (Header)null;
092               if (o instanceof Header o2)
093                  h = o2;
094               else if (o instanceof NameValuePair o3)
095                  h = BasicHeader.of(o3);
096               else
097                  h = new SerializedHeader(n, o, hm.getSerializer().orElse(defaultPartSerializer).getPartSession(), ps, true);
098               res.addHeader(h);
099            }
100         } catch (Exception e) {
101            throw new InternalServerError(e, "Could not set header ''{0}''", n);
102         }
103      }
104
105      var bm = rm.getContentMethod();
106
107      if (nn(bm)) {
108         var m = bm.getGetter();
109         try {
110            var pt = m.getParameterTypes();
111            if (pt.length == 1) {
112               var ptt = pt[0];
113               if (ptt == OutputStream.class)
114                  m.invoke(output, res.getOutputStream());
115               else if (ptt == Writer.class)
116                  m.invoke(output, res.getWriter());
117               return 1;
118            }
119            res.setContent(m.invoke(output));
120         } catch (Exception e) {
121            throw new InternalServerError(e, "Could not get content.");
122         }
123      }
124
125      return NEXT;  // Let PojoProcessor serialize it.
126   }
127
128   private static Iterable<?> iterate(Object o) {
129      if (o == null)
130         return Collections.emptyList();
131      if (o instanceof Map<?,?> m)
132         return m.entrySet();
133      if (isArray(o))
134         return l((Object[])o);
135      if (o instanceof Collection<?> c)
136         return c;
137      throw new InternalServerError("Could not iterate over Headers of type ''{0}''", cn(o));
138   }
139}