blob: d0f669022514bafd50bd89a2052ebe42d8d02a73 [file] [log] [blame]
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.rest.reshandlers;
import static org.apache.juneau.internal.ObjectUtils.*;
import static org.apache.juneau.internal.StringUtils.*;
import static org.apache.juneau.httppart.HttpPartType.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import org.apache.juneau.http.*;
import org.apache.juneau.httppart.*;
import org.apache.juneau.httppart.bean.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.http.exception.*;
import org.apache.juneau.rest.util.FinishablePrintWriter;
import org.apache.juneau.rest.util.FinishableServletOutputStream;
import org.apache.juneau.serializer.*;
/**
* Response handler for POJOs not handled by other handlers.
*
* <p>
* This uses the serializers defined on the response to serialize the POJO.
*
* <p>
* The {@link Serializer} used is based on the <c>Accept</c> header on the request.
*
* <p>
* The <c>Content-Type</c> header is set to the mime-type defined on the selected serializer based on the
* <c>produces</c> value passed in through the constructor.
*
* <ul class='seealso'>
* <li class='link'>{@doc juneau-rest-server.RestMethod.MethodReturnTypes}
* </ul>
*/
public class DefaultHandler implements ResponseHandler {
@SuppressWarnings("resource")
@Override /* ResponseHandler */
public boolean handle(RestRequest req, RestResponse res) throws IOException, InternalServerError, NotAcceptable {
SerializerGroup g = res.getSerializers();
String accept = req.getHeaders().getString("Accept", "");
SerializerMatch sm = g.getSerializerMatch(accept);
HttpPartSchema schema = null;
Object o = res.getOutput();
ResponseBeanMeta rm = res.getResponseMeta();
if (rm == null)
rm = req.getResponseBeanMeta(o);
if (rm != null) {
boolean isThrowable = rm.getClassMeta().isType(Throwable.class);
if (isThrowable) {
res.setHeaderSafe("Exception-Name", rm.getClassMeta().getName());
res.setHeaderSafe("Exception-Message", ((Throwable)o).getMessage());
}
ResponseBeanPropertyMeta stm = rm.getStatusMethod();
if (stm != null) {
try {
res.setStatus((int)stm.getGetter().invoke(o));
} catch (Exception e) {
throw new InternalServerError(e, "Could not get status.");
}
} else if (rm.getCode() != 0) {
res.setStatus(rm.getCode());
}
for (ResponseBeanPropertyMeta hm : rm.getHeaderMethods()) {
try {
Object ho = hm.getGetter().invoke(o);
String n = hm.getPartName();
if ("*".equals(n) && ho instanceof Map) {
@SuppressWarnings("rawtypes") Map m = (Map)ho;
for (Object key : m.keySet()) {
String k = stringify(key);
Object v = m.get(key);
HttpPartSchema s = hm.getSchema().getProperty(k);
res.setHeader(new HttpPart(k, RESPONSE_HEADER, s, hm.getSerializer(req.getPartSerializer()), req.getSerializerSessionArgs(), v));
}
} else {
res.setHeader(new HttpPart(n, RESPONSE_HEADER, hm.getSchema(), hm.getSerializer(req.getPartSerializer()), req.getSerializerSessionArgs(), ho));
}
} catch (Exception e) {
throw new InternalServerError(e, "Could not set header ''{0}''", hm.getPartName());
}
}
ResponseBeanPropertyMeta bm = rm.getBodyMethod();
if (bm != null) {
Method m = bm.getGetter();
try {
Class<?>[] pt = m.getParameterTypes();
if (pt.length == 1) {
Class<?> ptt = pt[0];
if (ptt == OutputStream.class)
m.invoke(o, res.getOutputStream());
else if (ptt == Writer.class)
m.invoke(o, res.getWriter());
return true;
}
o = m.invoke(o);
} catch (Exception e) {
throw new InternalServerError(e, "Could not get body.");
}
}
schema = rm.getSchema();
}
if (sm != null) {
Serializer s = sm.getSerializer();
MediaType mediaType = res.getMediaType();
if (mediaType == null)
mediaType = sm.getMediaType();
MediaType responseType = s.getResponseContentType();
if (responseType == null)
responseType = mediaType;
res.setContentType(responseType.toString());
try {
if (req.isPlainText())
res.setContentType("text/plain");
SerializerSession session = s.createSession(
SerializerSessionArgs
.create()
.properties(req.getAttributes())
.javaMethod(req.getJavaMethod())
.locale(req.getLocale())
.timeZone(req.getHeaders().getTimeZone())
.mediaType(mediaType)
.streamCharset(res.getCharset())
.schema(schema)
.debug(req.isDebug() ? true : null)
.uriContext(req.getUriContext())
.useWhitespace(req.isPlainText() ? true : null)
.resolver(req.getVarResolverSession())
);
for (Map.Entry<String,String> h : session.getResponseHeaders().entrySet())
res.setHeaderSafe(h.getKey(), h.getValue());
if (! session.isWriterSerializer()) {
if (req.isPlainText()) {
FinishablePrintWriter w = res.getNegotiatedWriter();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
session.serialize(o, baos);
w.write(StringUtils.toSpacedHex(baos.toByteArray()));
w.flush();
w.finish();
} else {
FinishableServletOutputStream os = res.getNegotiatedOutputStream();
session.serialize(o, os);
os.flush();
os.finish();
}
} else {
FinishablePrintWriter w = res.getNegotiatedWriter();
session.serialize(o, w);
w.flush();
w.finish();
}
} catch (SerializeException e) {
throw new InternalServerError(e);
}
return true;
}
// Non-existent Accept or plain/text can just be serialized as-is.
if (o != null && (isEmpty(accept) || accept.startsWith("text/plain"))) {
String out = null;
if (isEmpty(res.getContentType()))
res.setContentType("text/plain");
out = req.getBeanSession().getClassMetaForObject(o).toString(o);
FinishablePrintWriter w = res.getNegotiatedWriter();
w.append(out);
w.flush();
w.finish();
return true;
}
throw new NotAcceptable(
"Unsupported media-type in request header ''Accept'': ''{0}''\n\tSupported media-types: {1}",
req.getHeaders().getString("Accept", ""), g.getSupportedMediaTypes()
);
}
}