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 */ 017 package org.apache.logging.log4j.core.appender; 018 019 import java.io.IOException; 020 import java.io.OutputStream; 021 022 /** 023 * Manage an OutputStream so that it can be shared by multiple Appenders and will 024 * allow appenders to reconfigure without requiring a new stream. 025 */ 026 public class OutputStreamManager extends AbstractManager { 027 028 private OutputStream os; 029 030 private byte[] footer = null; 031 private byte[] header = null; 032 033 protected OutputStreamManager(final OutputStream os, final String streamName) { 034 super(streamName); 035 this.os = os; 036 } 037 038 /** 039 * Create a Manager. 040 * 041 * @param name The name of the stream to manage. 042 * @param data The data to pass to the Manager. 043 * @param factory The factory to use to create the Manager. 044 * @param <T> The type of the OutputStreamManager. 045 * @return An OutputStreamManager. 046 */ 047 public static <T> OutputStreamManager getManager(final String name, final T data, 048 final ManagerFactory<? extends OutputStreamManager, T> factory) { 049 return AbstractManager.getManager(name, factory, data); 050 } 051 052 /** 053 * Set the header to write when the stream is opened. 054 * @param header The header. 055 */ 056 public synchronized void setHeader(final byte[] header) { 057 if (header != null) { 058 this.header = header; 059 try { 060 this.os.write(header, 0, header.length); 061 } catch (final IOException ioe) { 062 LOGGER.error("Unable to write header", ioe); 063 } 064 } 065 } 066 067 /** 068 * Set the footer to write when the stream is closed. 069 * @param footer The footer. 070 */ 071 public synchronized void setFooter(final byte[] footer) { 072 if (footer != null) { 073 this.footer = footer; 074 } 075 } 076 077 /** 078 * Default hook to write footer during close. 079 */ 080 @Override 081 public void releaseSub() { 082 if (footer != null) { 083 write(footer); 084 } 085 close(); 086 } 087 088 /** 089 * Returns the status of the stream. 090 * @return true if the stream is open, false if it is not. 091 */ 092 public boolean isOpen() { 093 return getCount() > 0; 094 } 095 096 protected OutputStream getOutputStream() { 097 return os; 098 } 099 100 protected void setOutputStream(final OutputStream os) { 101 this.os = os; 102 if (header != null) { 103 try { 104 this.os.write(header, 0, header.length); 105 } catch (final IOException ioe) { 106 LOGGER.error("Unable to write header", ioe); 107 } 108 } 109 } 110 111 /** 112 * Some output streams synchronize writes while others do not. Synchronizing here insures that 113 * log events won't be intertwined. 114 * @param bytes The serialized Log event. 115 * @param offset The offset into the byte array. 116 * @param length The number of bytes to write. 117 * @throws AppenderRuntimeException if an error occurs. 118 */ 119 protected synchronized void write(final byte[] bytes, final int offset, final int length) { 120 //System.out.println("write " + count); 121 try { 122 os.write(bytes, offset, length); 123 } catch (final IOException ex) { 124 final String msg = "Error writing to stream " + getName(); 125 throw new AppenderRuntimeException(msg, ex); 126 } 127 } 128 129 /** 130 * Some output streams synchronize writes while others do not. Synchronizing here insures that 131 * log events won't be intertwined. 132 * @param bytes The serialized Log event. 133 * @throws AppenderRuntimeException if an error occurs. 134 */ 135 protected void write(final byte[] bytes) { 136 write(bytes, 0, bytes.length); 137 } 138 139 protected void close() { 140 if (os == System.out || os == System.err) { 141 return; 142 } 143 try { 144 os.close(); 145 } catch (final IOException ex) { 146 LOGGER.error("Unable to close stream " + getName() + ". " + ex); 147 } 148 } 149 150 /** 151 * Flush any buffers. 152 */ 153 public void flush() { 154 try { 155 os.flush(); 156 } catch (final IOException ex) { 157 final String msg = "Error flushing stream " + getName(); 158 throw new AppenderRuntimeException(msg, ex); 159 } 160 } 161 }