diff --git a/.gitignore b/.gitignore
index e06912d790..2851564bc8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,9 +46,9 @@ hs_err_pid*
.project
.settings
-# no log files:
+# no log/data files:
*.log
*.evio
-bin/evio2hipotest
-export.sh
+# scons litter:
+.sconsign.dblite
diff --git a/build-coatjava.sh b/build-coatjava.sh
index 9b04f4aec7..c2c8046b94 100755
--- a/build-coatjava.sh
+++ b/build-coatjava.sh
@@ -208,6 +208,10 @@ for pom in $(find common-tools -name pom.xml); do
# install_jars $pom $prefix_dir/lib/services
fi
done
+
+# install JNI stuff:
+$(cd common-tools/clas-io/target && cp *.so ../../../coatjava/lib >& /dev/null || cp *.dylib ../../../coatjava/lib)
+
echo "installed coatjava to: $prefix_dir"
echo "COATJAVA SUCCESSFULLY BUILT !"
diff --git a/common-tools/clas-io/pom.xml b/common-tools/clas-io/pom.xml
index f4972146be..d258c9b800 100644
--- a/common-tools/clas-io/pom.xml
+++ b/common-tools/clas-io/pom.xml
@@ -60,6 +60,43 @@
compile
+
+ org.jlab
+ groot
+ 4.0.5
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.6.1
+
+
+ scons-1
+ install
+ exec
+
+ target/native
+
+
+
+ scons-2
+ install
+ exec
+
+
+
+
+
+
+ scons
+
+
+
+
+
diff --git a/common-tools/clas-io/sconscript b/common-tools/clas-io/sconscript
new file mode 100644
index 0000000000..8d7196c1ad
--- /dev/null
+++ b/common-tools/clas-io/sconscript
@@ -0,0 +1,19 @@
+Import('env')
+
+javah = env.Command('native', 'src/main/java/org/jlab/io/root/HoistJNI.java', ['javac -h $TARGET $SOURCE', Delete('src/main/java/org/jlab/io/root/HoistJNI.class')])
+AlwaysBuild(javah)
+
+if 'JAVA_HOME' in env['ENV']:
+ env['JAVA_HOME'] = env['ENV']['JAVA_HOME']
+ env.Append(CPPPATH = ["$JAVA_HOME/include", "$JAVA_HOME/include/linux"])
+
+elif env['PLATFORM'] == 'darwin':
+ env.ParseConfig("echo -I`/usr/libexec/java_home`/include")
+ env.ParseConfig("echo -I`/usr/libexec/java_home`/include/darwin")
+
+if env['PLATFORM'] == 'darwin':
+ env.ParseConfig("echo -Wl,-rpath,`root-config --libdir`")
+
+env.Append(CPPPATH = ['native'])
+env.ParseConfig("root-config --libs --cflags")
+env.SharedLibrary('hoistJNI', Glob('*.cpp'))
diff --git a/common-tools/clas-io/sconstruct b/common-tools/clas-io/sconstruct
new file mode 100644
index 0000000000..b3c8906270
--- /dev/null
+++ b/common-tools/clas-io/sconstruct
@@ -0,0 +1,5 @@
+import os
+
+env = Environment(ENV=os.environ)
+Export('env')
+env.SConscript('sconscript', variant_dir='target', duplicate=0)
diff --git a/common-tools/clas-io/src/main/cpp/hoistJNI.cpp b/common-tools/clas-io/src/main/cpp/hoistJNI.cpp
new file mode 100644
index 0000000000..a961db8dac
--- /dev/null
+++ b/common-tools/clas-io/src/main/cpp/hoistJNI.cpp
@@ -0,0 +1,132 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "org_jlab_io_root_HoistJNI.h"
+
+class concurrentMap
+{
+ std::mutex m_;
+ std::unordered_map objs;
+
+public:
+ TObject* get(std::string k) {
+ std::unique_lock lock(m_);
+ return objs[k];
+ }
+
+ void set(std::string k, TObject* v) {
+ std::unique_lock lock(m_);
+ objs[k] = v;
+ }
+};
+
+concurrentMap objects;
+
+static __attribute__((constructor)) void init() {
+ gSystem->ResetSignals();
+ ROOT::EnableThreadSafety();
+ gROOT->SetBatch(true);
+}
+
+JNIEXPORT void JNICALL Java_org_jlab_io_root_HoistJNI_createFile (JNIEnv *env, jobject thisObj, jstring jfname) {
+ const char *fname = env->GetStringUTFChars(jfname, NULL);
+ TFile* ff = new TFile(fname, "RECREATE");
+ objects.set(fname, ff);
+ env->ReleaseStringUTFChars(jfname, fname);
+}
+
+JNIEXPORT void JNICALL Java_org_jlab_io_root_HoistJNI_closeFile (JNIEnv *env, jobject thisObj, jstring jfname) {
+ const char *fname = env->GetStringUTFChars(jfname, NULL);
+ ((TFile*) objects.get(fname))->Close();
+ env->ReleaseStringUTFChars(jfname, fname);
+}
+
+JNIEXPORT void JNICALL Java_org_jlab_io_root_HoistJNI_mkdir( JNIEnv *env, jobject thisObj, jstring jfname, jstring jpath) {
+ const char *fname = env->GetStringUTFChars(jfname, NULL);
+ const char *path = env->GetStringUTFChars(jpath, NULL);
+ ((TFile*) objects.get(fname))->mkdir(path);
+ env->ReleaseStringUTFChars(jfname, fname);
+ env->ReleaseStringUTFChars(jpath, path);
+}
+
+JNIEXPORT void JNICALL Java_org_jlab_io_root_HoistJNI_writeH1F (JNIEnv *env, jobject thisObj, jstring jfname, jstring jpath,
+ jstring jname, jstring jtitle, jint nbins, jdouble xmin, jdouble xmax, jfloatArray jdata) {
+
+ const char *fname = env->GetStringUTFChars(jfname, NULL);
+ const char *path = env->GetStringUTFChars(jpath, NULL);
+ const char *name = env->GetStringUTFChars(jname, NULL);
+ const char *title = env->GetStringUTFChars(jtitle, NULL);
+
+ float *cdata = env->GetFloatArrayElements(jdata, NULL);
+
+ TFile* ff = (TFile*) objects.get(fname);
+ if(!ff->Get(path))
+ ff->mkdir(path);
+ ff->cd(path);
+
+ TH1F* h1 = new TH1F(name, title, nbins, xmin, xmax);
+ jsize length = env->GetArrayLength(jdata);
+ double nentries = 0;
+
+ for(int ib=0;ibSetBinContent(ib+1, cdata[ib]);
+ nentries += cdata[ib];
+ }
+
+ h1->SetEntries(nentries);
+ h1->Write("",TObject::kOverwrite);
+
+ env->ReleaseFloatArrayElements(jdata, cdata, 0);
+ env->ReleaseStringUTFChars(jfname, fname);
+ env->ReleaseStringUTFChars(jpath, path);
+ env->ReleaseStringUTFChars(jname, name);
+ env->ReleaseStringUTFChars(jtitle, title);
+}
+
+JNIEXPORT void JNICALL Java_org_jlab_io_root_HoistJNI_writeH2F (JNIEnv *env, jobject thisObj, jstring jfname, jstring jpath,
+ jstring jname, jstring jtitle, jint nxbins, jdouble xmin, jdouble xmax, jint nybins, jdouble ymin, jdouble ymax, jfloatArray jdata) {
+
+ const char *fname = env->GetStringUTFChars(jfname, NULL);
+ const char *path = env->GetStringUTFChars(jpath, NULL);
+ const char *name = env->GetStringUTFChars(jname, NULL);
+ const char *title = env->GetStringUTFChars(jtitle, NULL);
+
+ float *cdata = env->GetFloatArrayElements(jdata, NULL);
+
+ TFile* ff = (TFile*) objects.get(fname);
+ if(!ff->Get(path))
+ ff->mkdir(path);
+ ff->cd(path);
+
+ TH2F* h2 = new TH2F(name, title, nxbins, xmin, xmax, nybins, ymin, ymax);
+ double nentries = 0;
+ int ii = 0;
+
+ for(int ix=0;ixSetBinContent(ix+1, iy+1, cdata[ii]);
+ nentries += cdata[ii++];
+ }
+
+ h2->SetEntries(nentries);
+ h2->Write("",TObject::kOverwrite);
+
+ env->ReleaseFloatArrayElements(jdata, cdata, 0);
+ env->ReleaseStringUTFChars(jfname, fname);
+ env->ReleaseStringUTFChars(jpath, path);
+ env->ReleaseStringUTFChars(jname, name);
+ env->ReleaseStringUTFChars(jtitle, title);
+}
+
diff --git a/common-tools/clas-io/src/main/java/org/jlab/io/root/Hoist.java b/common-tools/clas-io/src/main/java/org/jlab/io/root/Hoist.java
new file mode 100644
index 0000000000..9587bceeaa
--- /dev/null
+++ b/common-tools/clas-io/src/main/java/org/jlab/io/root/Hoist.java
@@ -0,0 +1,62 @@
+package org.jlab.io.root;
+
+import org.jlab.groot.data.H1F;
+import org.jlab.groot.data.H2F;
+
+/**
+ * Copied from https://github.com/drewkenjo/j2root
+ */
+public class Hoist {
+
+ HoistJNI hoist = new HoistJNI();
+
+ private String fname;
+ private String path = "";
+
+ public Hoist(String fname) {
+ this.fname = fname;
+ hoist.createFile(fname);
+ }
+
+ public void close() {
+ hoist.closeFile(fname);
+ }
+
+ public void mkdir(String path) {
+ path = path.replaceFirst("^/*","").replaceAll("/*\\$","");
+ hoist.mkdir(fname, path);
+ this.path = path;
+ }
+
+ public void cd(String path) {
+ this.path = path.replaceFirst("^/*","").replaceAll("/*\\$","");
+ }
+
+ public void write(H1F h1) {
+ String fullpath = path + "/" + h1.getName();
+ int ind = fullpath.lastIndexOf("/");
+ String relpath = fullpath.substring(0,ind);
+ relpath = relpath.replaceFirst("^/*","").replaceAll("/*\\$","");
+ String name = fullpath.substring(ind+1);
+ hoist.writeH1F(fname, relpath, name,
+ h1.getXaxis().getNBins(), h1.getXaxis().min(), h1.getXaxis().max(),
+ h1.getData());
+ }
+
+ public void write(H2F h2) {
+ String fullpath = path + "/" + h2.getName();
+ int ind = fullpath.lastIndexOf("/");
+ String relpath = fullpath.substring(0,ind);
+ relpath = relpath.replaceFirst("^/*","").replaceAll("/*\\$","");
+ String name = fullpath.substring(ind+1);
+ int xsize = h2.getDataSize(0), ysize = h2.getDataSize(1), ii = 0;
+ float[] data = new float[xsize*ysize];
+ for(int ix=0;ix