/* ************************************************************************
#
# 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 io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import divconq.ctp.CtpAdapter;
import divconq.ctp.CtpCommand;
import divconq.ctp.f.BlockCommand;
import divconq.ctp.f.CtpFCommand;
import divconq.ctp.f.FileDescriptor;
import divconq.filestore.IFileStoreDriver;
import divconq.filestore.IFileStoreFile;
import divconq.lang.op.OperationContext;
import divconq.script.StackEntry;
import divconq.struct.Struct;
import divconq.struct.scalar.NullStruct;
import divconq.work.TaskRun;
import divconq.xml.XElement;
// this is not tested much
public class CtpStreamDest extends BaseStream implements IStreamDest, ChannelFutureListener {
protected CtpAdapter adapter = null;
protected boolean userelpath = false;
protected boolean finalFlag = false;
protected String relpath = null;
protected OperationContext ctx = null;
public CtpStreamDest(CtpAdapter adapter, OperationContext ctx) {
this.adapter = adapter;
this.ctx = ctx;
}
@Override
public void init(StackEntry stack, XElement el, boolean autorelative) {
if (autorelative || stack.boolFromElement(el, "Relative", false) || el.getName().startsWith("X")) {
this.relpath = "";
this.userelpath = true;
}
Struct src = stack.refFromElement(el, "RelativeTo");
if ((src != null) && !(src instanceof NullStruct)) {
if (src instanceof IFileStoreDriver)
this.relpath = "";
else if (src instanceof IFileStoreFile)
this.relpath = ((IFileStoreFile)src).getPath();
else
this.relpath = src.toString();
this.userelpath = true;
}
}
@Override
public void close() {
// TODO return the channel to it's manager, if not already - if we didn't get a FINAL below then
// tell manager it is bad - close channel, read/write state unknown
this.adapter = null;
super.close();
}
@Override
public void operationComplete(ChannelFuture future) throws Exception {
OperationContext.set(this.ctx);
TaskRun trun = this.ctx.getTaskRun();
if (trun == null) {
System.out.println("Error - stream task missing RUN");
}
else if (future.isSuccess()) {
if (this.finalFlag)
trun.complete();
else
trun.resume();
}
else {
trun.kill("ERROR sending - DONE sending! " + future.cause());
}
}
@Override
public ReturnOption handle(FileDescriptor file, ByteBuf data) {
this.finalFlag = (file == FileDescriptor.FINAL);
// TODO build up a buffer of at least N size before flush...FINAL always flushes
CtpCommand cmd = CtpFCommand.STREAM_FINAL;
if (!this.finalFlag) {
cmd = new BlockCommand();
((BlockCommand)cmd).copyAttributes(file);
((BlockCommand)cmd).setData(data);
}
System.out.println("Sending FINAL: " + this.finalFlag);
try {
this.adapter.sendCommandNotify(cmd, this);
}
catch (Exception x) {
System.out.println("Ctp-F Client stream error: " + x);
}
/*
try {
// TODO if errors...ABORT
if (this.isEmptyResult()) {
this.adapter.sendCommand(new FinalCommand());
OperationContext.get().getTaskRun().complete();
return;
}
IFileStoreFile file = this.getResult();
BlockCommand cmd = new BlockCommand();
FileSelection selection = this.selector.selection();
// TODO if CTP_F_ATTR_PREFERED then use session settings - from adapter?
if (selection.hasAttr(CtpConstants.CTP_F_ATTR_PATH) || selection.hasAttr(CtpConstants.CTP_F_ATTR_PREFERED))
cmd.setPath(file.path().subpath(this.relativeTo).toString());
if (selection.hasAttr(CtpConstants.CTP_F_ATTR_IS_FOLDER) || selection.hasAttr(CtpConstants.CTP_F_ATTR_PREFERED))
cmd.setIsFolder(file.isFolder());
if (selection.hasAttr(CtpConstants.CTP_F_ATTR_SIZE) || selection.hasAttr(CtpConstants.CTP_F_ATTR_PREFERED))
cmd.setSize(file.getSize());
if (selection.hasAttr(CtpConstants.CTP_F_ATTR_MODTIME) || selection.hasAttr(CtpConstants.CTP_F_ATTR_PREFERED))
cmd.setModTime(file.getModificationTime().getMillis());
if (selection.hasAttr(CtpConstants.CTP_F_ATTR_PERMISSIONS) || selection.hasAttr(CtpConstants.CTP_F_ATTR_PREFERED))
cmd.setPermissions(CtpConstants.CTP_F_PERMISSIONS_READ & CtpConstants.CTP_F_PERMISSIONS_WRITE); // TODO file.getPermissions());
if (selection.hasAttr(CtpConstants.CTP_F_ATTR_DATA)) {
// send headers
this.adapter.sendCommand(cmd);
// send block 1
cmd = new BlockCommand();
ByteBuf d = Hub.instance.getBufferAllocator().buffer(24);
d.writeLong(1);
d.writeLong(2);
d.writeLong(3);
cmd.setData(d);
this.adapter.sendCommand(cmd);
// progress 1
this.adapter.sendCommand(new ProgressCommand(33));
// send block 2
cmd = new BlockCommand();
d = Hub.instance.getBufferAllocator().buffer(24);
d.writeLong(1);
d.writeLong(2);
d.writeLong(3);
cmd.setData(d);
this.adapter.sendCommand(cmd);
// progress 2
this.adapter.sendCommand(new ProgressCommand(66));
// send block 3
cmd = new BlockCommand();
d = Hub.instance.getBufferAllocator().buffer(24);
d.writeLong(1);
d.writeLong(2);
d.writeLong(3);
cmd.setData(d);
this.adapter.sendCommand(cmd);
// progress 3
this.adapter.sendCommand(new ProgressCommand(99));
// send end
cmd = new BlockCommand();
cmd.setEof(true);
this.adapter.sendCommandNotify(cmd, this.future);
}
else {
cmd.setEof(true);
this.adapter.sendCommandNotify(cmd, this.future);
}
}
catch (Exception x) {
System.out.println("Ctp-F Server error: " + x);
}
*/
return ReturnOption.AWAIT;
}
@Override
public void read() {
// we are terminal, no downstream should call us
OperationContext.get().getTaskRun().kill("File destination cannot be a source");
}
@Override
public void execute() {
// TODO optimize if upstream is local file
this.upstream.read();
}
}