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