001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.common.util; 017 018import java.io.IOException; 019import java.io.InputStream; 020import org.modeshape.common.CommonI18n; 021import org.modeshape.common.annotation.NotThreadSafe; 022import org.modeshape.common.logging.Logger; 023 024/** 025 * An {@link InputStream} implementation that wraps another stream and makes sure that {@link java.io.InputStream#close()} 026 * is always called on the wrapped stream when there is no more content to read or when an unexpected exception occurs. 027 */ 028@NotThreadSafe 029public class SelfClosingInputStream extends InputStream { 030 031 private static final Logger LOGGER = Logger.getLogger(SelfClosingInputStream.class); 032 033 private final InputStream stream; 034 035 /** 036 * Create a self-closing {@link InputStream} that wraps another input stream. 037 * 038 * @param stream the wrapped {@link java.io.InputStream}; may not be null. 039 */ 040 public SelfClosingInputStream( InputStream stream ) { 041 assert stream != null; 042 this.stream = stream; 043 } 044 045 @Override 046 public int available() throws IOException { 047 try { 048 return stream.available(); 049 } catch (IOException e) { 050 closeStream(); 051 throw e; 052 } catch (RuntimeException e) { 053 closeStream(); 054 throw e; 055 } 056 } 057 058 @Override 059 public void close() throws IOException { 060 stream.close(); 061 } 062 063 @Override 064 public int hashCode() { 065 return stream.hashCode(); 066 } 067 068 @Override 069 public void mark( int readlimit ) { 070 try { 071 stream.mark(readlimit); 072 } catch (RuntimeException e) { 073 closeStream(); 074 throw e; 075 } 076 } 077 078 @Override 079 public boolean markSupported() { 080 try { 081 return stream.markSupported(); 082 } catch (RuntimeException e) { 083 closeStream(); 084 throw e; 085 } 086 } 087 088 @Override 089 public int read( byte[] b, 090 int off, 091 int len ) throws IOException { 092 try { 093 int result = stream.read(b, off, len); 094 if (result == -1) { 095 // the end of the stream has been reached ... 096 closeStream(); 097 } 098 return result; 099 } catch (IOException e) { 100 closeStream(); 101 throw e; 102 } catch (RuntimeException e) { 103 closeStream(); 104 throw e; 105 } 106 } 107 108 @Override 109 public int read( byte[] b ) throws IOException { 110 try { 111 int result = stream.read(b); 112 if (result == -1) { 113 // the end of the stream has been reached ... 114 closeStream(); 115 } 116 return result; 117 } catch (IOException e) { 118 closeStream(); 119 throw e; 120 } catch (RuntimeException e) { 121 closeStream(); 122 throw e; 123 } 124 } 125 126 @Override 127 public int read() throws IOException { 128 try { 129 int result = stream.read(); 130 if (result == -1) { 131 // the end of the stream has been reached ... 132 closeStream(); 133 } 134 return result; 135 } catch (IOException e) { 136 closeStream(); 137 throw e; 138 } catch (RuntimeException e) { 139 closeStream(); 140 throw e; 141 } 142 } 143 144 @Override 145 public void reset() throws IOException { 146 try { 147 stream.reset(); 148 } catch (IOException e) { 149 closeStream(); 150 throw e; 151 } catch (RuntimeException e) { 152 closeStream(); 153 throw e; 154 } 155 } 156 157 @Override 158 public long skip( long n ) throws IOException { 159 try { 160 return stream.skip(n); 161 } catch (IOException e) { 162 closeStream(); 163 throw e; 164 } catch (RuntimeException e) { 165 closeStream(); 166 throw e; 167 } 168 } 169 170 @Override 171 public String toString() { 172 return stream.toString(); 173 } 174 175 /** 176 * Returns the stream that this instance wraps. 177 * 178 * @return an {@link InputStream} instance, never null. 179 */ 180 public InputStream wrappedStream() { 181 return stream; 182 } 183 184 private void closeStream() { 185 try { 186 stream.close(); 187 } catch (IOException e) { 188 LOGGER.error(e, CommonI18n.errorClosingWrappedStream); 189 } 190 } 191}