View Javadoc
1   package nom.tam.fits.compress;
2   
3   import java.io.BufferedInputStream;
4   import java.io.ByteArrayOutputStream;
5   import java.io.FilterInputStream;
6   import java.io.IOException;
7   import java.io.InputStream;
8   import java.io.OutputStream;
9   import java.nio.charset.Charset;
10  import java.util.logging.Level;
11  import java.util.logging.Logger;
12  
13  /*
14   * #%L
15   * nom.tam FITS library
16   * %%
17   * Copyright (C) 1996 - 2024 nom-tam-fits
18   * %%
19   * This is free and unencumbered software released into the public domain.
20   *
21   * Anyone is free to copy, modify, publish, use, compile, sell, or
22   * distribute this software, either in source code form or as a compiled
23   * binary, for any purpose, commercial or non-commercial, and by any
24   * means.
25   *
26   * In jurisdictions that recognize copyright laws, the author or authors
27   * of this software dedicate any and all copyright interest in the
28   * software to the public domain. We make this dedication for the benefit
29   * of the public at large and to the detriment of our heirs and
30   * successors. We intend this dedication to be an overt act of
31   * relinquishment in perpetuity of all present and future rights to this
32   * software under copyright law.
33   *
34   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
35   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
36   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
37   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
38   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
39   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
40   * OTHER DEALINGS IN THE SOFTWARE.
41   * #L%
42   */
43  
44  import static nom.tam.util.LoggerHelper.getLogger;
45  
46  /**
47   * (<i>for internal use</i>) Ensures that input streams aren't left open when decompressing with an external system
48   * tool, such as the UNIX <b>compress</b> or <b>bzip2</b> commands. It is discouraged to use system tools for
49   * decompressing such files, especially since we have native Java implementations through Apache's
50   * <b>commons-compress</b> classes. The system tools are not portable, whereas the <b>commons-compress</b>
51   * implementation is. Therefore, you should neer really need to use this class, which is provided only for compatibility
52   * with earlier versions of this library.
53   * 
54   * @deprecated Needed only by deprecated compression classes. And it should not have visibility outside of this package
55   *                 anyway.
56   */
57  @Deprecated
58  public class CloseIS extends FilterInputStream {
59  
60      private static final Logger LOG = getLogger(CloseIS.class);
61  
62      private static final int COPY_BUFFER_SIZE = 64 * 1024;
63  
64      private InputStream output;
65  
66      private OutputStream input;
67  
68      private String errorText;
69  
70      private IOException exception;
71  
72      private final Thread stdError;
73  
74      private final Thread copier;
75  
76      private final Process proc;
77  
78      /**
79       * Instantiates a new thread that will watch and close the input stream whenever the process using it compeletes.
80       * 
81       * @param proc       The process that is using the input stream
82       * @param compressed the compressed input stream that is used by the process.
83       */
84      @SuppressWarnings("resource")
85      public CloseIS(Process proc, final InputStream compressed) {
86          super(new BufferedInputStream(proc.getInputStream(), CompressionManager.ONE_MEGABYTE));
87          if (compressed == null) {
88              throw new NullPointerException();
89          }
90          this.proc = proc;
91          final InputStream error = proc.getErrorStream();
92          output = proc.getInputStream();
93          input = proc.getOutputStream();
94          stdError = new Thread(new Runnable() {
95  
96              @Override
97              public void run() {
98                  try {
99                      ByteArrayOutputStream bytes = new ByteArrayOutputStream();
100                     byte[] buffer = new byte[COPY_BUFFER_SIZE];
101                     int len;
102                     while ((len = error.read(buffer, 0, buffer.length)) >= 0) {
103                         bytes.write(buffer, 0, len);
104                     }
105                     error.close();
106                     errorText = new String(bytes.toByteArray(), Charset.defaultCharset());
107                 } catch (IOException e) {
108                     exception = e;
109                 }
110             }
111         });
112         // Now copy everything in a separate thread.
113         copier = new Thread(new Runnable() {
114 
115             @Override
116             public void run() {
117                 try {
118                     byte[] buffer = new byte[COPY_BUFFER_SIZE];
119                     int len;
120                     while ((len = compressed.read(buffer, 0, buffer.length)) >= 0) {
121                         input.write(buffer, 0, len);
122                     }
123                     input.close();
124                 } catch (IOException e) {
125                     exception = e;
126                 }
127                 try {
128                     compressed.close();
129                 } catch (IOException e) {
130                     if (exception == null) {
131                         exception = e;
132                     }
133                 }
134             }
135         });
136         start();
137     }
138 
139     /**
140      * start all threads.
141      */
142     private void start() {
143         stdError.start();
144         copier.start();
145     }
146 
147     @Override
148     public int read() throws IOException {
149         int result = 0;
150         try {
151             result = super.read();
152             return result;
153         } catch (IOException e) {
154             result = -1;
155             throw e;
156         } finally {
157             handledOccuredException(result);
158         }
159     }
160 
161     private void handledOccuredException(int result) throws IOException {
162         int exitValue = 0;
163         if (result < 0) {
164             try {
165                 stdError.join();
166                 copier.join();
167                 exitValue = proc.exitValue();
168             } catch (Exception e) {
169                 LOG.log(Level.WARNING, "could not join the stream processes", e);
170             }
171         }
172         if (exception != null || exitValue != 0) {
173             if (errorText != null && !errorText.trim().isEmpty()) {
174                 throw new IOException(errorText, exception);
175             }
176             if (exception == null) {
177                 throw new IOException("exit value was " + exitValue);
178             }
179             throw exception;
180         }
181     }
182 
183     @Override
184     public int read(byte[] b, int off, int len) throws IOException {
185         int result = 0;
186         try {
187             result = super.read(b, off, len);
188             return result;
189         } catch (IOException e) {
190             throw e;
191         } finally {
192             handledOccuredException(result);
193         }
194     }
195 
196     @Override
197     public void close() throws IOException {
198         super.close();
199         input.close();
200         output.close();
201     }
202 
203 }