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.marshall.serializer; 018 019import static org.apache.juneau.commons.utils.IoUtils.*; 020import static org.apache.juneau.commons.utils.Shorts.*; 021import java.io.*; 022import java.nio.charset.*; 023 024import org.apache.juneau.commons.io.*; 025import org.apache.juneau.commons.utils.*; 026 027/** 028 * A wrapper around an object that a serializer sends its output to. 029 * 030 * <p> 031 * For character-based serializers, the output object can be any of the following: 032 * <ul> 033 * <li>{@link Writer} 034 * <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream. 035 * </ul> 036 * 037 * <p> 038 * For stream-based serializers, the output object must be an {@link OutputStream}. 039 * 040 * <h5 class='section'>See Also:</h5><ul> 041 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a> 042 * </ul> 043 */ 044@SuppressWarnings({ 045 "resource" // outputStream/writer wrap the caller-supplied output; the caller retains ownership and close() only flushes. 046}) 047public class SerializerPipe implements Closeable { 048 049 private final Object output; 050 051 private OutputStream outputStream; 052 private Writer writer; 053 private final Charset charset; 054 055 /** 056 * Stream-based constructor. 057 * 058 * @param output The object to pipe the serializer output to. 059 */ 060 SerializerPipe(Object output) { 061 this.output = output; 062 this.charset = null; 063 } 064 065 /** 066 * Writer-based constructor. 067 * 068 * @param output The object to pipe the serializer output to. 069 * @param streamCharset The charset to use when writing to an {@link OutputStream}. If <jk>null</jk>, defaults to UTF-8. 070 */ 071 SerializerPipe(Object output, Charset streamCharset) { 072 this.output = output; 073 this.charset = or(streamCharset, UTF8); 074 } 075 076 /** 077 * Closes the output pipe. 078 */ 079 @Override /* Overridden from Closeable */ 080 public void close() { 081 try { 082 IoUtils.flush(writer, outputStream); 083 } catch (IOException e) { 084 throw brex(e); 085 } 086 } 087 088 /** 089 * Wraps the specified output object inside an output stream. 090 * 091 * <p> 092 * Subclasses can override this method to implement their own specialized output streams. 093 * 094 * <p> 095 * This method can be used if the output object is an {@link OutputStream}. 096 * 097 * @return 098 * The output object wrapped in an output stream. 099 * Calling {@link OutputStream#close()} on the returned object simply flushes the response and does not close 100 * the underlying stream. 101 * @throws IOException If object could not be converted to an output stream. 102 */ 103 public OutputStream getOutputStream() throws IOException { 104 if (output == null) 105 throw ioex("Output cannot be null."); 106 107 if (output instanceof OutputStream output2) 108 outputStream = output2; 109 else 110 throw ioex("Cannot convert object of type %s to an OutputStream.", cn(output)); 111 112 return new NoCloseOutputStream(outputStream); 113 } 114 115 /** 116 * Returns the raw output object passed into this session. 117 * 118 * @return The raw output object passed into this session. 119 */ 120 public Object getRawOutput() { return output; } 121 122 /** 123 * Wraps the specified output object inside a writer. 124 * 125 * <p> 126 * Subclasses can override this method to implement their own specialized writers. 127 * 128 * <p> 129 * This method can be used if the output object is any of the following class types: 130 * <ul> 131 * <li>{@link Writer} 132 * <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream. 133 * </ul> 134 * 135 * @return 136 * The output object wrapped in a writer. 137 * Calling {@link Writer#close()} on the returned object simply flushes the response and does not close 138 * the underlying writer. 139 * @throws SerializeException If object could not be converted to a writer. 140 */ 141 public Writer getWriter() throws SerializeException { 142 if (output == null) 143 throw new SerializeException("Output cannot be null."); 144 145 if (output instanceof Writer output2) 146 writer = output2; 147 else if (output instanceof OutputStream output2) 148 writer = new OutputStreamWriter(output2, charset); 149 else 150 throw new SerializeException("Cannot convert object of type " + cn(output) + " to a Writer."); 151 152 return new NoCloseWriter(writer); 153 } 154 155 /** 156 * Overwrites the output stream in this pipe. 157 * 158 * <p> 159 * Used when wrapping the stream returned by {@link #getOutputStream()} so that the wrapped stream will be flushed 160 * when {@link #close()} is called. 161 * 162 * @param outputStream The wrapped stream. 163 */ 164 public void setOutputStream(OutputStream outputStream) { this.outputStream = outputStream; } 165 166 /** 167 * Overwrites the writer in this pipe. 168 * 169 * <p> 170 * Used when wrapping the writer returned by {@link #getWriter()} so that the wrapped writer will be flushed 171 * and closed when {@link #close()} is called. 172 * 173 * @param writer The wrapped writer. 174 */ 175 public void setWriter(Writer writer) { this.writer = writer; } 176}