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.*; 030import org.apache.juneau.serializer.*; 031 032/** 033 * Response handler for POJOs not handled by other handlers. 034 * 035 * <p> 036 * This uses the serializers defined on the response to serialize the POJO. 037 * 038 * <p> 039 * The {@link Serializer} used is based on the <code>Accept</code> header on the request. 040 * 041 * <p> 042 * The <code>Content-Type</code> header is set to the mime-type defined on the selected serializer based on the 043 * <code>produces</code> value passed in through the constructor. 044 * 045 * <h5 class='section'>See Also:</h5> 046 * <ul> 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 ResponseBeanPropertyMeta stm = rm.getStatusMethod(); 069 if (stm != null) { 070 try { 071 res.setStatus((int)stm.getGetter().invoke(o)); 072 } catch (Exception e) { 073 throw new InternalServerError(e, "Could not get status."); 074 } 075 } else if (rm.getCode() != 0) { 076 res.setStatus(rm.getCode()); 077 } 078 079 for (ResponseBeanPropertyMeta hm : rm.getHeaderMethods()) { 080 try { 081 Object ho = hm.getGetter().invoke(o); 082 String n = hm.getPartName(); 083 if ("*".equals(n) && ho instanceof Map) { 084 @SuppressWarnings("rawtypes") Map m = (Map)ho; 085 for (Object key : m.keySet()) { 086 String k = asString(key); 087 Object v = m.get(key); 088 HttpPartSchema s = hm.getSchema().getProperty(k); 089 res.setHeader(new HttpPart(k, RESPONSE_HEADER, s, hm.getSerializer(req.getPartSerializer()), req.getSerializerSessionArgs(), v)); 090 } 091 } else { 092 res.setHeader(new HttpPart(n, RESPONSE_HEADER, hm.getSchema(), hm.getSerializer(req.getPartSerializer()), req.getSerializerSessionArgs(), ho)); 093 } 094 } catch (Exception e) { 095 throw new InternalServerError(e, "Could not set header ''{0}''", hm.getPartName()); 096 } 097 } 098 099 ResponseBeanPropertyMeta bm = rm.getBodyMethod(); 100 101 if (bm != null) { 102 Method m = bm.getGetter(); 103 try { 104 Class<?>[] pt = m.getParameterTypes(); 105 if (pt.length == 1) { 106 Class<?> ptt = pt[0]; 107 if (ptt == OutputStream.class) 108 m.invoke(o, res.getOutputStream()); 109 else if (ptt == Writer.class) 110 m.invoke(o, res.getWriter()); 111 return true; 112 } 113 o = m.invoke(o); 114 } catch (Exception e) { 115 throw new InternalServerError(e, "Could not get body."); 116 } 117 } 118 119 schema = rm.getSchema(); 120 } 121 122 if (sm != null) { 123 Serializer s = sm.getSerializer(); 124 MediaType mediaType = res.getMediaType(); 125 if (mediaType == null) 126 mediaType = sm.getMediaType(); 127 128 MediaType responseType = s.getResponseContentType(); 129 if (responseType == null) 130 responseType = mediaType; 131 132 res.setContentType(responseType.toString()); 133 134 try { 135 RequestProperties p = res.getProperties(); 136 if (req.isPlainText()) 137 res.setContentType("text/plain"); 138 p.append("mediaType", mediaType).append("characterEncoding", res.getCharacterEncoding()); 139 140 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)); 141 142 for (Map.Entry<String,String> h : session.getResponseHeaders().entrySet()) 143 res.setHeader(h.getKey(), h.getValue()); 144 145 if (! session.isWriterSerializer()) { 146 if (req.isPlainText()) { 147 FinishablePrintWriter w = res.getNegotiatedWriter(); 148 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 149 session.serialize(o, baos); 150 w.write(StringUtils.toSpacedHex(baos.toByteArray())); 151 w.flush(); 152 w.finish(); 153 } else { 154 FinishableServletOutputStream os = res.getNegotiatedOutputStream(); 155 session.serialize(o, os); 156 os.flush(); 157 os.finish(); 158 } 159 } else { 160 FinishablePrintWriter w = res.getNegotiatedWriter(); 161 session.serialize(o, w); 162 w.flush(); 163 w.finish(); 164 } 165 } catch (SerializeException e) { 166 throw new InternalServerError(e); 167 } 168 return true; 169 } 170 171 // Non-existent Accept or plain/text can just be serialized as-is. 172 if (o != null && (isEmpty(accept) || accept.startsWith("text/plain"))) { 173 String out = null; 174 if (isEmpty(res.getContentType())) 175 res.setContentType("text/plain"); 176 out = req.getBeanSession().getClassMetaForObject(o).toString(o); 177 FinishablePrintWriter w = res.getNegotiatedWriter(); 178 w.append(out); 179 w.flush(); 180 w.finish(); 181 return true; 182 } 183 184 throw new NotAcceptable( 185 "Unsupported media-type in request header ''Accept'': ''{0}''\n\tSupported media-types: {1}", 186 req.getHeaders().getString("Accept", ""), g.getSupportedMediaTypes() 187 ); 188 } 189}