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.server.processor; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020import static org.apache.juneau.commons.utils.StringUtils.*; 021 022import java.io.*; 023 024import org.apache.juneau.commons.http.*; 025import org.apache.juneau.http.header.*; 026import org.apache.juneau.http.response.*; 027import org.apache.juneau.marshall.html.*; 028import org.apache.juneau.marshall.httppart.*; 029import org.apache.juneau.marshall.marshaller.*; 030import org.apache.juneau.marshall.serializer.*; 031import org.apache.juneau.rest.server.*; 032import org.apache.juneau.rest.server.util.*; 033 034/** 035 * Response handler for plain-old Java objects. 036 * 037 * <h5 class='section'>See Also:</h5><ul> 038 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ResponseProcessors">Response Processors</a> 039 * </ul> 040 */ 041@SuppressWarnings({ 042 "resource" // SerializedPojoProcessor manages Closeable resources 043}) 044public class SerializedPojoProcessor implements CatchAllResponseProcessor { 045 046 @Override /* Overridden from ResponseProcessor */ 047 @SuppressWarnings({ 048 "java:S3776" // Cognitive complexity acceptable for POJO serialization logic 049 }) 050 public int process(RestOpSession opSession) throws IOException, NotAcceptable, BasicHttpException { 051 RestRequest req = opSession.getRequest(); 052 RestResponse res = opSession.getResponse(); 053 SerializerMatch sm = res.getSerializerMatch().orElse(null); 054 HttpPartSchema schema = res.getContentSchema().orElse(null); 055 056 Object o = res.getContent(Object.class); 057 058 if (nn(sm)) { 059 try { 060 Serializer s = sm.getSerializer(); 061 MediaType mediaType = res.getMediaType(); 062 if (mediaType == null) 063 mediaType = sm.getMediaType(); 064 065 MediaType responseType = s.getResponseContentType(); 066 if (responseType == null) 067 responseType = mediaType; 068 069 if (req.isPlainText()) 070 res.setHeader(ContentType.TEXT_PLAIN); 071 else 072 res.setHeader(ContentType.of(responseType.toString())); 073 074 // @formatter:off 075 SerializerSession session = s 076 .createSession() 077 .properties(req.getSerializerSessionPropertyMap()) 078 .javaMethod(req.getOpContext().getJavaMethod()) 079 .locale(req.getLocale()) 080 .timeZone(req.getTimeZone().orElse(null)) 081 .mediaType(mediaType) 082 .apply(WriterSerializerSession.Builder.class, x -> x.streamCharset(res.getCharset()).useWhitespace(req.isPlainText() ? true : null)) 083 .schema(schema) 084 .debug(req.isDebugMarshalling() ? true : null) 085 .uriContext(req.getUriContext()) 086 .resolver(req.getVarResolverSession()) 087 .apply(HtmlDocSerializerSession.Builder.class, x -> req.getAttribute(RestRequest.CSP_NONCE_ATTR).as(String.class).ifPresent(x::nonce)) 088 .build(); 089 // @formatter:on 090 091 for (var h : session.getResponseHeaders().entrySet()) 092 res.addHeader(h.getKey(), h.getValue()); 093 094 if (! session.isWriterSerializer()) { 095 if (req.isPlainText()) { 096 FinishablePrintWriter w = res.getNegotiatedWriter(); 097 var baos = new ByteArrayOutputStream(); 098 session.write(o, baos); 099 w.write(toSpacedHex(baos.toByteArray())); 100 w.flush(); 101 w.finish(); 102 } else { 103 FinishableServletOutputStream os = res.getNegotiatedOutputStream(); 104 session.write(o, os); 105 os.flush(); 106 os.finish(); 107 } 108 } else { 109 FinishablePrintWriter w = res.getNegotiatedWriter(); 110 session.write(o, w); 111 w.flush(); 112 w.finish(); 113 } 114 } catch (SerializeException e) { 115 throw new InternalServerError(e); 116 } 117 return FINISHED; 118 } 119 120 if (o == null) 121 return FINISHED; 122 123 throw new NotAcceptable("Unsupported media-type in request header 'Accept': '%s'\n\tSupported media-types: %s", req.getHeaderParam("Accept").orElse(""), 124 Json5.of(res.getOpContext().getSerializers().getSupportedMediaTypes())); 125 } 126}