/* ************************************************************************ # # DivConq # # http://divconq.com/ # # Copyright: # Copyright 2014 eTimeline, LLC. All rights reserved. # # License: # See the license.txt file in the project's top-level directory for details. # # Authors: # * Andy White # ************************************************************************ */ package divconq.ctp.stream; import io.netty.buffer.ByteBuf; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.StandardOpenOption; import divconq.ctp.f.FileDescriptor; import divconq.filestore.IFileCollection; import divconq.filestore.IFileStoreFile; import divconq.filestore.local.FileSystemFile; import divconq.hub.Hub; import divconq.lang.op.FuncCallback; import divconq.lang.op.OperationContext; import divconq.script.StackEntry; import divconq.xml.XElement; public class FileSourceStream extends BaseStream implements IStreamSource { protected IFileCollection source = null; protected IFileStoreFile current = null; protected FileChannel in = null; protected long insize = 0; protected long inprog = 0; public FileSourceStream(IFileCollection src) { this.source = src; } // for use with dcScript @Override public void init(StackEntry stack, XElement el) { // anything we need to gleam from the xml? } @Override public ReturnOption handle(FileDescriptor file, ByteBuf data) { // we are at top of stream, nothing to do here return ReturnOption.CONTINUE; } @Override public void close() { //System.out.println("File SRC killed"); // TODO if (this.in != null) try { this.in.close(); } catch (IOException x) { } this.in = null; this.current = null; this.source = null; super.close(); } /** * Someone downstream wants more data */ @Override public void read() { if (this.source == null) { this.downstream.handle(FileDescriptor.FINAL, null); return; } if (this.current == null) { this.source.next(new FuncCallback<IFileStoreFile>() { @Override public void callback() { if (this.hasErrors()) { OperationContext.get().getTaskRun().kill(); return; } FileSourceStream.this.readFile(this.getResult()); } }); } // folders are handled in 1 msg, so we wouldn't get here in second or later call to a file else if (this.current instanceof FileSystemFile) FileSourceStream.this.readLocalFile(); else { FileSourceStream.this.readOtherFile(); } } public void readFile(IFileStoreFile file) { this.current = file; // if we reached the end of the collection then finish if (this.current == null) { this.downstream.handle(FileDescriptor.FINAL, null); } else if (this.current.isFolder()) { FileDescriptor fref = FileDescriptor.fromFileStore(this.current); fref.setIsFolder(true); fref.setPath(this.current.path().subpath(this.source.path())); if (this.downstream.handle(fref, null) == ReturnOption.CONTINUE) { FileSourceStream.this.current = null; OperationContext.get().getTaskRun().resume(); } } else if (this.current instanceof FileSystemFile) FileSourceStream.this.readLocalFile(); else { FileSourceStream.this.readOtherFile(); } } public void readOtherFile() { // TODO abstract out so this class is a FileCollectionSourceStream and we // use it pull out the source streams of files, which we then use as if upstream from us } // release data if error public void readLocalFile() { FileSystemFile fs = (FileSystemFile) this.current; if (this.in == null) { this.insize = fs.getSize(); // As a source we are responsible for progress tracking OperationContext.get().setAmountCompleted(0); try { this.in = FileChannel.open(fs.localPath(), StandardOpenOption.READ); } catch (IOException x) { OperationContext.get().getTaskRun().kill("Unable to read source file " + x); return; } } while (true) { // TODO sizing? ByteBuf data = Hub.instance.getBufferAllocator().heapBuffer(32768); ByteBuffer buffer = ByteBuffer.wrap(data.array(), data.arrayOffset(), data.capacity()); int pos = -1; try { pos = (int)this.in.read(buffer); } catch (IOException x1) { OperationContext.get().getTaskRun().kill("Problem reading source file: " + x1); data.release(); return; } FileDescriptor fref = FileDescriptor.fromFileStore(this.current); fref.setPath(this.current.path().subpath(this.source.path())); System.out.println("writing: " + fref.getPath() + " from: " + this.inprog); if (pos == -1) { try { this.in.close(); } catch (IOException x) { OperationContext.get().getTaskRun().kill("Problem closing source file: " + x); data.release(); return; } OperationContext.get().setAmountCompleted(100); fref.setEof(true); this.current = null; this.in = null; this.insize = 0; this.inprog = 0; } else { this.inprog += pos; data.writerIndex(pos); OperationContext.get().setAmountCompleted((int)(this.inprog * 100 / this.insize)); } if (this.downstream.handle(fref, data) != ReturnOption.CONTINUE) break; if (this.current == null) { // we need the next file OperationContext.get().getTaskRun().resume(); // wait on the implied request break; } } } }