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.reshandlers; 014 015import static org.apache.juneau.internal.ObjectUtils.*; 016import static org.apache.juneau.internal.StringUtils.*; 017import static org.apache.juneau.httppart.HttpPartType.*; 018 019import java.io.*; 020import java.lang.reflect.*; 021import java.util.*; 022 023import org.apache.juneau.http.*; 024import org.apache.juneau.httppart.*; 025import org.apache.juneau.httppart.bean.*; 026import org.apache.juneau.internal.*; 027import org.apache.juneau.rest.*; 028import org.apache.juneau.http.exception.*; 029import org.apache.juneau.rest.util.FinishablePrintWriter; 030import org.apache.juneau.rest.util.FinishableServletOutputStream; 031import org.apache.juneau.serializer.*; 032 033/** 034 * Response handler for POJOs not handled by other handlers. 035 * 036 * <p> 037 * This uses the serializers defined on the response to serialize the POJO. 038 * 039 * <p> 040 * The {@link Serializer} used is based on the <c>Accept</c> header on the request. 041 * 042 * <p> 043 * The <c>Content-Type</c> header is set to the mime-type defined on the selected serializer based on the 044 * <c>produces</c> value passed in through the constructor. 045 * 046 * <ul class='seealso'> 047 * <li class='link'>{@doc juneau-rest-server.RestMethod.MethodReturnTypes} 048 * </ul> 049 */ 050public class DefaultHandler implements ResponseHandler { 051 052 @SuppressWarnings("resource") 053 @Override /* ResponseHandler */ 054 public boolean handle(RestRequest req, RestResponse res) throws IOException, InternalServerError, NotAcceptable { 055 SerializerGroup g = res.getSerializers(); 056 String accept = req.getHeaders().getString("Accept", ""); 057 SerializerMatch sm = g.getSerializerMatch(accept); 058 HttpPartSchema schema = null; 059 060 Object o = res.getOutput(); 061 062 ResponseBeanMeta rm = res.getResponseMeta(); 063 if (rm == null) 064 rm = req.getResponseBeanMeta(o); 065 066 if (rm != null) { 067 068 boolean isThrowable = rm.getClassMeta().isType(Throwable.class); 069 if (isThrowable) { 070 res.setHeader("Exception-Name", rm.getClassMeta().getName()); 071 res.setHeader("Exception-Message", ((Throwable)o).getMessage()); 072 } 073 074 ResponseBeanPropertyMeta stm = rm.getStatusMethod(); 075 if (stm != null) { 076 try { 077 res.setStatus((int)stm.getGetter().invoke(o)); 078 } catch (Exception e) { 079 throw new InternalServerError(e, "Could not get status."); 080 } 081 } else if (rm.getCode() != 0) { 082 res.setStatus(rm.getCode()); 083 } 084 085 for (ResponseBeanPropertyMeta hm : rm.getHeaderMethods()) { 086 try { 087 Object ho = hm.getGetter().invoke(o); 088 String n = hm.getPartName(); 089 if ("*".equals(n) && ho instanceof Map) { 090 @SuppressWarnings("rawtypes") Map m = (Map)ho; 091 for (Object key : m.keySet()) { 092 String k = stringify(key); 093 Object v = m.get(key); 094 HttpPartSchema s = hm.getSchema().getProperty(k); 095 res.setHeader(new HttpPart(k, RESPONSE_HEADER, s, hm.getSerializer(req.getPartSerializer()), req.getSerializerSessionArgs(), v)); 096 } 097 } else { 098 res.setHeader(new HttpPart(n, RESPONSE_HEADER, hm.getSchema(), hm.getSerializer(req.getPartSerializer()), req.getSerializerSessionArgs(), ho)); 099 } 100 } catch (Exception e) { 101 throw new InternalServerError(e, "Could not set header ''{0}''", hm.getPartName()); 102 } 103 } 104 105 ResponseBeanPropertyMeta bm = rm.getBodyMethod(); 106 107 if (bm != null) { 108 Method m = bm.getGetter(); 109 try { 110 Class<?>[] pt = m.getParameterTypes(); 111 if (pt.length == 1) { 112 Class<?> ptt = pt[0]; 113 if (ptt == OutputStream.class) 114 m.invoke(o, res.getOutputStream()); 115 else if (ptt == Writer.class) 116 m.invoke(o, res.getWriter()); 117 return true; 118 } 119 o = m.invoke(o); 120 } catch (Exception e) { 121 throw new InternalServerError(e, "Could not get body."); 122 } 123 } 124 125 schema = rm.getSchema(); 126 } 127 128 if (sm != null) { 129 Serializer s = sm.getSerializer(); 130 MediaType mediaType = res.getMediaType(); 131 if (mediaType == null) 132 mediaType = sm.getMediaType(); 133 134 MediaType responseType = s.getResponseContentType(); 135 if (responseType == null) 136 responseType = mediaType; 137 138 res.setContentType(responseType.toString()); 139 140 try { 141 if (req.isPlainText()) 142 res.setContentType("text/plain"); 143 SerializerSession session = s.createSession( 144 SerializerSessionArgs 145 .create() 146 .properties(req.getAttributes()) 147 .javaMethod(req.getJavaMethod()) 148 .locale(req.getLocale()) 149 .timeZone(req.getHeaders().getTimeZone()) 150 .mediaType(mediaType) 151 .streamCharset(res.getCharset()) 152 .schema(schema) 153 .debug(req.isDebug() ? true : null) 154 .uriContext(req.getUriContext()) 155 .useWhitespace(req.isPlainText() ? true : null) 156 .resolver(req.getVarResolverSession()) 157 ); 158 159 for (Map.Entry<String,String> h : session.getResponseHeaders().entrySet()) 160 res.setHeader(h.getKey(), h.getValue()); 161 162 if (! session.isWriterSerializer()) { 163 if (req.isPlainText()) { 164 FinishablePrintWriter w = res.getNegotiatedWriter(); 165 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 166 session.serialize(o, baos); 167 w.write(StringUtils.toSpacedHex(baos.toByteArray())); 168 w.flush(); 169 w.finish(); 170 } else { 171 FinishableServletOutputStream os = res.getNegotiatedOutputStream(); 172 session.serialize(o, os); 173 os.flush(); 174 os.finish(); 175 } 176 } else { 177 FinishablePrintWriter w = res.getNegotiatedWriter(); 178 session.serialize(o, w); 179 w.flush(); 180 w.finish(); 181 } 182 } catch (SerializeException e) { 183 throw new InternalServerError(e); 184 } 185 return true; 186 } 187 188 // Non-existent Accept or plain/text can just be serialized as-is. 189 if (o != null && (isEmpty(accept) || accept.startsWith("text/plain"))) { 190 String out = null; 191 if (isEmpty(res.getContentType())) 192 res.setContentType("text/plain"); 193 out = req.getBeanSession().getClassMetaForObject(o).toString(o); 194 FinishablePrintWriter w = res.getNegotiatedWriter(); 195 w.append(out); 196 w.flush(); 197 w.finish(); 198 return true; 199 } 200 201 throw new NotAcceptable( 202 "Unsupported media-type in request header ''Accept'': ''{0}''\n\tSupported media-types: {1}", 203 req.getHeaders().getString("Accept", ""), g.getSupportedMediaTypes() 204 ); 205 } 206}