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