com.ziclix.python.sql.pipe.Pipe Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jython-slim Show documentation
Show all versions of jython-slim Show documentation
Jython is an implementation of the high-level, dynamic, object-oriented
language Python written in 100% Pure Java, and seamlessly integrated with
the Java platform. It thus allows you to run Python on any Java platform.
/*
* Jython Database Specification API 2.0
*
*
* Copyright (c) 2001 brian zimmer
*
*/
package com.ziclix.python.sql.pipe;
import org.python.core.*;
import com.ziclix.python.sql.*;
import com.ziclix.python.sql.util.*;
/**
* Manager for a Sink and Source. The Pipe creates a Queue through which the Source
* can feed data to the Sink. Both Sink and Source run in their own thread and can
* are completely independent of the other. When the Source pushes None onto the
* Queue, the piping is stopped and the Sink finishes processing all the remaining
* data. This class is especially useful for loading/copying data from one database
* or table to another.
*
* @author brian zimmer
*/
public class Pipe {
/**
* Default empty constructor.
*/
public Pipe() {
}
/**
* Start the processing of the Source to the Sink.
*
* @param source the data generator
* @param sink the consumer of the data
* @return the number of rows seen (this includes the header row)
*/
public PyObject pipe(Source source, Sink sink) {
Queue queue = new Queue();
SourceRunner sourceRunner = new SourceRunner(queue, source);
SinkRunner sinkRunner = new SinkRunner(queue, sink);
sourceRunner.start();
sinkRunner.start();
try {
sourceRunner.join();
} catch (InterruptedException e) {
queue.close();
throw zxJDBC.makeException(e);
}
try {
sinkRunner.join();
} catch (InterruptedException e) {
queue.close();
throw zxJDBC.makeException(e);
}
/*
* This is interesting territory. I originally tried to store the the Throwable in the Thread instance
* and then re-throw it here, but whenever I tried, I would get an NPE in the construction of the
* PyTraceback required for the PyException. I tried calling .fillInStackTrace() but that didn't work
* either. So I'm left with getting the String representation and throwing that. At least it gives
* the relevant error messages, but the stack is lost. This might have something to do with a Java
* issue I don't completely understand, such as what happens for an Exception whose Thread is no longer
* running? Anyways, if anyone knows what to do I would love to hear about it.
*/
if (sourceRunner.threwException()) {
throw zxJDBC.makeException(sourceRunner.getException().toString());
}
if (sinkRunner.threwException()) {
throw zxJDBC.makeException(sinkRunner.getException().toString());
}
// if the source count is -1, no rows were queried
if (sinkRunner.getCount() == 0) {
return Py.newInteger(0);
}
// Assert that both sides handled the same number of rows. I know doing the check up front kinda defeats
// the purpose of the assert, but there's no need to create the buffer if I don't need it and I still
// want to throw the AssertionError if required
if ((sourceRunner.getCount() - sinkRunner.getCount()) != 0) {
Integer[] counts = {Integer.valueOf(sourceRunner.getCount()),
Integer.valueOf(sinkRunner.getCount())};
String msg = zxJDBC.getString("inconsistentRowCount", counts);
Py.assert_(Py.Zero, Py.newString(msg));
}
return Py.newInteger(sinkRunner.getCount());
}
}
/**
* Class PipeRunner
*
* @author
* @date $today.date$
* @copyright 2001 brian zimmer
*/
abstract class PipeRunner extends Thread {
/**
* Field counter
*/
protected int counter;
/**
* Field queue
*/
protected Queue queue;
/**
* Field exception
*/
protected Throwable exception;
/**
* Constructor PipeRunner
*
* @param Queue queue
*/
public PipeRunner(Queue queue) {
this.counter = 0;
this.queue = queue;
this.exception = null;
}
/**
* The total number of rows handled.
*/
public int getCount() {
return this.counter;
}
/**
* Method run
*/
@Override
public void run() {
try {
this.pipe();
} catch (QueueClosedException e) {
/*
* thrown by a closed queue when any operation is performed. we know
* at this point that nothing else can happen to the queue and that
* both producer and consumer will stop since one closed the queue
* by throwing an exception (below) and the other is here.
*/
return;
} catch (Throwable e) {
this.exception = e.fillInStackTrace();
this.queue.close();
}
}
/**
* Handle the source/destination specific copying.
*/
abstract protected void pipe() throws InterruptedException;
/**
* Return true if the thread terminated because of an uncaught exception.
*/
public boolean threwException() {
return this.exception != null;
}
/**
* Return the uncaught exception.
*/
public Throwable getException() {
return this.exception;
}
}
/**
* Class SourceRunner
*
* @author
* @date $today.date$
* @copyright 2001 brian zimmer
*/
class SourceRunner extends PipeRunner {
/**
* Field source
*/
protected Source source;
/**
* Constructor SourceRunner
*
* @param Queue queue
* @param Source source
*/
public SourceRunner(Queue queue, Source source) {
super(queue);
this.source = source;
}
/**
* Method pipe
*
* @throws InterruptedException
*/
@Override
protected void pipe() throws InterruptedException {
PyObject row = Py.None;
this.source.start();
try {
while ((row = this.source.next()) != Py.None) {
this.queue.enqueue(row);
this.counter++;
}
} finally {
try {
this.queue.enqueue(Py.None);
} finally {
this.source.end();
}
}
}
}
/**
* Class SinkRunner
*
* @author
* @date $today.date$
* @copyright 2001 brian zimmer
*/
class SinkRunner extends PipeRunner {
/**
* Field sink
*/
protected Sink sink;
/**
* Constructor SinkRunner
*
* @param Queue queue
* @param Sink sink
*/
public SinkRunner(Queue queue, Sink sink) {
super(queue);
this.sink = sink;
}
/**
* Method pipe
*
* @throws InterruptedException
*/
@Override
protected void pipe() throws InterruptedException {
PyObject row = Py.None;
this.sink.start();
try {
while ((row = (PyObject) this.queue.dequeue()) != Py.None) {
this.sink.row(row);
this.counter++;
}
} finally {
this.sink.end();
}
}
}