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 ; 12 import java.io.File ; 13 import java.io.IOException ; 14 import java.io.OutputStream ; 15 import java.io.RandomAccessFile ; 16 import java.io.Writer ; 17 import java.io.InputStream ; 18 19 27 public class LargeText { 28 31 private interface Source { 32 Session open() throws IOException ; 33 long length(); 34 boolean exists(); 35 } 36 private final Source source; 37 38 private volatile boolean completed; 39 40 public LargeText(final File file, boolean completed) { 41 this.source = new Source() { 42 public Session open() throws IOException { 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 { 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 96 public long writeLogTo(long start, Writer w) throws IOException { 97 CountingOutputStream os = new CountingOutputStream(new WriterOutputStream(w)); 98 99 Session f = source.open(); 100 f.skip(start); 101 102 if(completed) { 103 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 129 public void doProgressText(StaplerRequest req, StaplerResponse rsp) throws IOException { 130 rsp.setContentType("text/plain"); 131 rsp.setCharacterEncoding("UTF-8"); 132 rsp.setStatus(HttpServletResponse.SC_OK); 133 134 if(!source.exists()) { 135 rsp.addHeader("X-Text-Size","0"); 137 rsp.addHeader("X-More-Data","true"); 138 return; 139 } 140 141 long start = 0; 142 String s = req.getParameter("start"); 143 if(s!=null) 144 start = Long.parseLong(s); 145 146 if(source.length() < start ) 147 start = 0; 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 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 176 private static final class HeadMark extends Mark { 177 public HeadMark(ByteBuf buf) { 178 super(buf); 179 } 180 181 185 void moveTo(Mark that, OutputStream os) throws IOException { 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 { 196 os.write(buf.buf,0,pos); 197 } 198 } 199 200 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 { 209 while(true) { 210 while(pos==buf.size) { 211 if(!buf.isFull()) { 212 return false; 214 } else { 215 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 { 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 255 private interface Session { 256 void close() throws IOException ; 257 void skip(long start) throws IOException ; 258 int read(byte[] buf) throws IOException ; 259 int read(byte[] buf, int offset, int length) throws IOException ; 260 } 261 262 265 private static final class FileSession implements Session { 266 private final RandomAccessFile file; 267 268 public FileSession(File file) throws IOException { 269 this.file = new RandomAccessFile (file,"r"); 270 } 271 272 public void close() throws IOException { 273 file.close(); 274 } 275 276 public void skip(long start) throws IOException { 277 file.seek(file.getFilePointer()+start); 278 } 279 280 public int read(byte[] buf) throws IOException { 281 return file.read(buf); 282 } 283 284 public int read(byte[] buf, int offset, int length) throws IOException { 285 return file.read(buf,offset,length); 286 } 287 } 288 289 292 private static final class BufferSession implements Session { 293 private final InputStream in; 294 295 public BufferSession(ByteBuffer buf) { 296 this.in = buf.newInputStream(); 297 } 298 299 300 public void close() throws IOException { 301 in.close(); 302 } 303 304 public void skip(long start) throws IOException { 305 in.skip(start); 306 } 307 308 public int read(byte[] buf) throws IOException { 309 return in.read(buf); 310 } 311 312 public int read(byte[] buf, int offset, int length) throws IOException { 313 return in.read(buf,offset,length); 314 } 315 } 316 } 317 | Popular Tags |