KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > hudson > model > LargeText


1 package hudson.model;
2
3 import hudson.util.CharSpool;
4 import hudson.util.CountingOutputStream;
5 import hudson.util.WriterOutputStream;
6 import hudson.util.ByteBuffer;
7 import hudson.util.LineEndNormalizingWriter;
8 import org.kohsuke.stapler.StaplerRequest;
9 import org.kohsuke.stapler.StaplerResponse;
10
11 import javax.servlet.http.HttpServletResponse JavaDoc;
12 import java.io.File JavaDoc;
13 import java.io.IOException JavaDoc;
14 import java.io.OutputStream JavaDoc;
15 import java.io.RandomAccessFile JavaDoc;
16 import java.io.Writer JavaDoc;
17 import java.io.InputStream JavaDoc;
18
19 /**
20  * Represents a large text data.
21  *
22  * <p>
23  * This class defines methods for handling progressive text update.
24  *
25  * @author Kohsuke Kawaguchi
26  */

27 public class LargeText {
28     /**
29      * Represents the data source of this text.
30      */

31     private interface Source {
32         Session open() throws IOException JavaDoc;
33         long length();
34         boolean exists();
35     }
36     private final Source source;
37
38     private volatile boolean completed;
39
40     public LargeText(final File JavaDoc file, boolean completed) {
41         this.source = new Source() {
42             public Session open() throws IOException JavaDoc {
43                 return new FileSession(file);
44             }
45
46             public long length() {
47                 return file.length();
48             }
49
50             public boolean exists() {
51                 return file.exists();
52             }
53         };
54         this.completed = completed;
55     }
56
57     public LargeText(final ByteBuffer memory, boolean completed) {
58         this.source = new Source() {
59             public Session open() throws IOException JavaDoc {
60                 return new BufferSession(memory);
61             }
62
63             public long length() {
64                 return memory.length();
65             }
66
67             public boolean exists() {
68                 return true;
69             }
70         };
71         this.completed = completed;
72     }
73
74     public void markAsComplete() {
75         completed = true;
76     }
77
78     public boolean isComplete() {
79         return completed;
80     }
81
82     /**
83      * Writes the tail portion of the file to the {@link Writer}.
84      *
85      * <p>
86      * The text file is assumed to be in the system default encoding.
87      *
88      * @param start
89      * The byte offset in the input file where the write operation starts.
90      *
91      * @return
92      * if the file is still being written, this method writes the file
93      * until the last newline character and returns the offset to start
94      * the next write operation.
95      */

96     public long writeLogTo(long start, Writer w) throws IOException JavaDoc {
97         CountingOutputStream os = new CountingOutputStream(new WriterOutputStream(w));
98
99         Session f = source.open();
100         f.skip(start);
101
102         if(completed) {
103             // write everything till EOF
104
byte[] buf = new byte[1024];
105             int sz;
106             while((sz=f.read(buf))>=0)
107                 os.write(buf,0,sz);
108         } else {
109             ByteBuf buf = new ByteBuf(null,f);
110             HeadMark head = new HeadMark(buf);
111             TailMark tail = new TailMark(buf);
112
113             while(tail.moveToNextLine(f)) {
114                 head.moveTo(tail,os);
115             }
116             head.finish(os);
117         }
118
119         f.close();
120         os.flush();
121
122         return os.getCount()+start;
123     }
124
125     /**
126      * Implements the progressive text handling.
127      * This method is used as a "web method" with progressiveText.jelly.
128      */

129     public void doProgressText(StaplerRequest req, StaplerResponse rsp) throws IOException JavaDoc {
130         rsp.setContentType("text/plain");
131         rsp.setCharacterEncoding("UTF-8");
132         rsp.setStatus(HttpServletResponse.SC_OK);
133
134         if(!source.exists()) {
135             // file doesn't exist yet
136
rsp.addHeader("X-Text-Size","0");
137             rsp.addHeader("X-More-Data","true");
138             return;
139         }
140
141         long start = 0;
142         String JavaDoc s = req.getParameter("start");
143         if(s!=null)
144             start = Long.parseLong(s);
145
146         if(source.length() < start )
147             start = 0; // text rolled over
148

149         CharSpool spool = new CharSpool();
150         long r = writeLogTo(start,spool);
151
152         rsp.addHeader("X-Text-Size",String.valueOf(r));
153         if(!completed)
154             rsp.addHeader("X-More-Data","true");
155
156         spool.writeTo(new LineEndNormalizingWriter(rsp.getWriter()));
157
158     }
159
160     /**
161      * Points to a byte in the buffer.
162      */

163     private static class Mark {
164         protected ByteBuf buf;
165         protected int pos;
166
167         public Mark(ByteBuf buf) {
168             this.buf = buf;
169         }
170     }
171
172     /**
173      * Points to the start of the region that's not committed
174      * to the output yet.
175      */

176     private static final class HeadMark extends Mark {
177         public HeadMark(ByteBuf buf) {
178             super(buf);
179         }
180
181         /**
182          * Moves this mark to 'that' mark, and writes the data
183          * to {@link OutputStream} if necessary.
184          */

185         void moveTo(Mark that, OutputStream os) throws IOException JavaDoc {
186             while(this.buf!=that.buf) {
187                 os.write(buf.buf,0,buf.size);
188                 buf = buf.next;
189                 pos = 0;
190             }
191
192             this.pos = that.pos;
193         }
194
195         void finish(OutputStream os) throws IOException JavaDoc {
196             os.write(buf.buf,0,pos);
197         }
198     }
199
200     /**
201      * Points to the end of the region.
202      */

203     private static final class TailMark extends Mark {
204         public TailMark(ByteBuf buf) {
205             super(buf);
206         }
207
208         boolean moveToNextLine(Session f) throws IOException JavaDoc {
209             while(true) {
210                 while(pos==buf.size) {
211                     if(!buf.isFull()) {
212                         // read until EOF
213
return false;
214                     } else {
215                         // read into the next buffer
216
buf = new ByteBuf(buf,f);
217                         pos = 0;
218                     }
219                 }
220                 byte b = buf.buf[pos++];
221                 if(b=='\r' || b=='\n')
222                     return true;
223             }
224         }
225     }
226
227     private static final class ByteBuf {
228         private final byte[] buf = new byte[1024];
229         private int size = 0;
230         private ByteBuf next;
231
232         public ByteBuf(ByteBuf previous, Session f) throws IOException JavaDoc {
233             if(previous!=null) {
234                 assert previous.next==null;
235                 previous.next = this;
236             }
237
238             while(!this.isFull()) {
239                 int chunk = f.read(buf, size, buf.length - size);
240                 if(chunk==-1)
241                     return;
242                 size+= chunk;
243             }
244         }
245
246         public boolean isFull() {
247             return buf.length==size;
248         }
249     }
250
251     /**
252      * Represents the read session of the {@link Source}.
253      * Methods generally follow the contracts of {@link InputStream}.
254      */

255     private interface Session {
256         void close() throws IOException JavaDoc;
257         void skip(long start) throws IOException JavaDoc;
258         int read(byte[] buf) throws IOException JavaDoc;
259         int read(byte[] buf, int offset, int length) throws IOException JavaDoc;
260     }
261
262     /**
263      * {@link Session} implementation over {@link RandomAccessFile}.
264      */

265     private static final class FileSession implements Session {
266         private final RandomAccessFile JavaDoc file;
267
268         public FileSession(File JavaDoc file) throws IOException JavaDoc {
269             this.file = new RandomAccessFile JavaDoc(file,"r");
270         }
271
272         public void close() throws IOException JavaDoc {
273             file.close();
274         }
275
276         public void skip(long start) throws IOException JavaDoc {
277             file.seek(file.getFilePointer()+start);
278         }
279
280         public int read(byte[] buf) throws IOException JavaDoc {
281             return file.read(buf);
282         }
283
284         public int read(byte[] buf, int offset, int length) throws IOException JavaDoc {
285             return file.read(buf,offset,length);
286         }
287     }
288
289     /**
290      * {@link Session} implementation over {@link ByteBuffer}.
291      */

292     private static final class BufferSession implements Session {
293         private final InputStream JavaDoc in;
294
295         public BufferSession(ByteBuffer buf) {
296             this.in = buf.newInputStream();
297         }
298
299
300         public void close() throws IOException JavaDoc {
301             in.close();
302         }
303
304         public void skip(long start) throws IOException JavaDoc {
305             in.skip(start);
306         }
307
308         public int read(byte[] buf) throws IOException JavaDoc {
309             return in.read(buf);
310         }
311
312         public int read(byte[] buf, int offset, int length) throws IOException JavaDoc {
313             return in.read(buf,offset,length);
314         }
315     }
316 }
317
Popular Tags