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.commons.vfs2.util; 018 019import java.io.BufferedOutputStream; 020import java.io.IOException; 021import java.io.OutputStream; 022import java.util.concurrent.atomic.AtomicBoolean; 023 024import org.apache.commons.vfs2.FileSystemException; 025 026/** 027 * An OutputStream that provides buffering and end-of-stream monitoring. 028 */ 029public class MonitorOutputStream extends BufferedOutputStream { 030 031 private final AtomicBoolean closed = new AtomicBoolean(); 032 033 /** 034 * Constructs a MonitorOutputStream from the passed OutputStream. 035 * 036 * @param out The output stream to wrap. 037 */ 038 public MonitorOutputStream(final OutputStream out) { 039 super(out); 040 } 041 042 /** 043 * Constructs a MonitorOutputStream from the passed OutputStream and with the specified buffer size. 044 * 045 * @param out The output stream to wrap. 046 * @param bufferSize The buffer size to use. 047 * @since 2.4 048 */ 049 public MonitorOutputStream(final OutputStream out, final int bufferSize) { 050 super(out, bufferSize); 051 } 052 053 /** 054 * Check if file is still open. 055 * <p> 056 * This is a workaround for an oddity with Java's BufferedOutputStream where you can write to even if the stream has 057 * been closed. 058 * </p> 059 * 060 * @throws FileSystemException if already closed. 061 * @since 2.0 062 */ 063 protected void assertOpen() throws FileSystemException { 064 if (isClosed()) { 065 throw new FileSystemException("vfs.provider/closed.error"); 066 } 067 } 068 069 /** 070 * Closes this output stream. 071 * <p> 072 * This makes sure the buffers are flushed, close the output stream and it will call {@link #onClose()} and re-throw 073 * last exception from any of the three. 074 * </p> 075 * <p> 076 * This does nothing if the stream is closed already. 077 * </p> 078 * 079 * @throws IOException if an IO error occurs. 080 */ 081 @Override 082 public void close() throws IOException { 083 // do not use super.close() 084 // on Java 8 it might throw self suppression, see JDK-8042377 085 // in older Java it silently ignores flush() errors 086 if (closed.getAndSet(true)) { 087 return; 088 } 089 090 IOException exc = null; 091 092 // flush the buffer and out stream 093 try { 094 super.flush(); 095 } catch (final IOException ioe) { 096 exc = ioe; 097 } 098 099 // close the out stream without using super.close() 100 try { 101 super.out.close(); 102 } catch (final IOException ioe) { 103 exc = ioe; 104 } 105 106 // Notify of end of output 107 try { 108 onClose(); 109 } catch (final IOException ioe) { 110 exc = ioe; 111 } 112 113 if (exc != null) { 114 throw exc; 115 } 116 } 117 118 /** 119 * @throws IOException if an error occurs. 120 * @since 2.0 121 */ 122 @Override 123 public synchronized void flush() throws IOException { 124 if (isClosed()) { 125 return; 126 } 127 super.flush(); 128 } 129 130 private boolean isClosed() { 131 return closed.get(); 132 } 133 134 /** 135 * Called after this stream is closed. 136 * <p> 137 * This implementation does nothing. 138 * </p> 139 * 140 * @throws IOException if an error occurs. 141 */ 142 // IOException is needed because subclasses may need to throw it 143 protected void onClose() throws IOException { 144 } 145 146 /** 147 * @param b The byte array. 148 * @throws IOException if an error occurs. 149 * @since 2.0 150 */ 151 @Override 152 public void write(final byte[] b) throws IOException { 153 assertOpen(); 154 super.write(b); 155 } 156 157 /** 158 * @param b The byte array. 159 * @param off The offset into the array. 160 * @param len The number of bytes to write. 161 * @throws IOException if an error occurs. 162 * @since 2.0 163 */ 164 @Override 165 public synchronized void write(final byte[] b, final int off, final int len) throws IOException { 166 assertOpen(); 167 super.write(b, off, len); 168 } 169 170 /** 171 * @param b The character to write. 172 * @throws IOException if an error occurs. 173 * @since 2.0 174 */ 175 @Override 176 public synchronized void write(final int b) throws IOException { 177 assertOpen(); 178 super.write(b); 179 } 180}