View Javadoc

1   /*
2    * $Id: LazyInputStream.java 20320 2010-11-24 15:03:31Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.module.pgp;
12  
13  import java.io.IOException;
14  import java.io.InputStream;
15  import java.io.PipedInputStream;
16  import java.io.PipedOutputStream;
17  
18  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
19  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong;
20  
21  import org.apache.commons.io.IOUtils;
22  
23  /**
24   * A {@link LazyInputStream} has a pipe that is written by an
25   * {@link OutputStreamWriter} but it is delay till a client of this object
26   * tries to read the stream.
27   * 
28   * The {@link LazyInputStream} uses a separate thread for writing on the pipe
29   * and delays it destruction till this {@link InputStream} is closed. In this way
30   * we avoid any problems with broken pipes.
31   */
32  public class LazyInputStream extends InputStream
33  {
34  
35      private PipedInputStream in;
36      private PipedOutputStream out;
37  
38      private AtomicBoolean startedCopying;
39      private Thread copyingThread;
40      private AtomicLong bytesRequested;
41  
42      private OutputStreamWriter writer;
43  
44      public LazyInputStream(OutputStreamWriter writer) throws IOException
45      {
46          this.in = new PipedInputStream();
47          this.out = new PipedOutputStream(this.in);
48          this.startedCopying = new AtomicBoolean(false);
49          this.bytesRequested = new AtomicLong(0);
50          this.writer = writer;
51      }
52  
53      private void copyRequest()
54      {
55          if (this.startedCopying.compareAndSet(false, true))
56          {
57              this.copyingThread = new WriteWork();
58              this.copyingThread.start();
59          }
60      }
61  
62      @Override
63      public int available() throws IOException
64      {
65          this.copyRequest();
66          return this.in.available();
67      }
68  
69      @Override
70      public void close() throws IOException
71      {
72          this.in.close();
73          this.copyingThread.interrupt();
74      }
75  
76      @Override
77      public synchronized void mark(int readlimit)
78      {
79          this.in.mark(readlimit);
80      }
81  
82      @Override
83      public boolean markSupported()
84      {
85          return this.in.markSupported();
86      }
87  
88      @Override
89      public int read() throws IOException
90      {
91          this.bytesRequested.addAndGet(1);
92          this.copyRequest();
93          return this.in.read();
94      }
95  
96      @Override
97      public int read(byte[] b, int off, int len) throws IOException
98      {
99          this.bytesRequested.addAndGet(len);
100         this.copyRequest();
101         return this.in.read(b, off, len);
102     }
103 
104     @Override
105     public int read(byte[] b) throws IOException
106     {
107         this.bytesRequested.addAndGet(b.length);
108         this.copyRequest();
109         return this.in.read(b);
110     }
111 
112     @Override
113     public synchronized void reset() throws IOException
114     {
115         this.in.reset();
116     }
117 
118     @Override
119     public long skip(long n) throws IOException
120     {
121         this.copyRequest();
122         return this.in.skip(n);
123     }
124 
125     private class WriteWork extends Thread
126     {
127         public void run()
128         {
129             try
130             {
131                 writer.initialize(out);
132                 
133                 boolean finishWriting = false;
134                 while (!finishWriting)
135                 {
136                     finishWriting = writer.write(out, bytesRequested);
137                 }
138             }
139             catch (Exception e)
140             {
141                 e.printStackTrace();
142             }
143             finally
144             {
145                 IOUtils.closeQuietly(out);
146                 while (!this.isInterrupted())
147                 {
148                     try
149                     {
150                         sleep(1000 * 60);
151                     }
152                     catch (InterruptedException e)
153                     {
154                     }
155                 }
156             }
157         }
158     }
159 }