diff --git a/Makefile b/Makefile
index 09aa037b487ff0c210810246275a77a76c882fdd..37d808572ffc98178f918aae1b68cf639091ed3e 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 # and kick off a recursive make
 # Also, "make src/all" turns into "make -C src all"
 
-SUBDIRS = src examples jni
+SUBDIRS = src examples src/bindings/jni
 
 include config.mk
 TOPLEVEL=.
diff --git a/SConstruct b/SConstruct
index c7e3ba5371c8e5419086e2373511bc5478ae0828..3120df44a885b706cc57534e9a0f548bcd1a0562 100644
--- a/SConstruct
+++ b/SConstruct
@@ -14,7 +14,7 @@ if platform.system() == 'Windows':
 vars = Variables(None, ARGUMENTS)
 vars.Add(PathVariable('DESTDIR', "Root directory to install in (useful for packaging scripts)", None, PathVariable.PathIsDirCreate))
 vars.Add(PathVariable('prefix', "Where to install in the FHS", "/usr/local", PathVariable.PathAccept))
-vars.Add(ListVariable('bindings', 'Language bindings to build', 'none', ['cpp', 'dotnet', 'perl', 'php', 'python', 'ruby']))
+vars.Add(ListVariable('bindings', 'Language bindings to build', 'none', ['cpp', 'dotnet', 'jni', 'perl', 'php', 'python', 'ruby']))
 vars.Add('python', 'Python interpreter', 'python')
 
 tools = ['default', 'scanreplace']
diff --git a/src/bindings/jni/ConfigureJNI.py b/src/bindings/jni/ConfigureJNI.py
new file mode 100644
index 0000000000000000000000000000000000000000..573bd682192e88618cba6c9b847128a98d6e08db
--- /dev/null
+++ b/src/bindings/jni/ConfigureJNI.py
@@ -0,0 +1,99 @@
+#!python 
+
+from __future__ import absolute_import, division, print_function
+
+import os
+import sys
+
+def walkDirs(path):
+    """helper function to get a list of all subdirectories"""
+    def addDirs(pathlist, dirname, names):
+        """internal function to pass to os.walk"""
+        print("in addDirs")
+        for n in names:
+            f = os.path.join(dirname, n)
+            if os.path.isdir(f):
+                pathlist.append(f)
+    pathlist = [path]
+    os.walk(path, addDirs, pathlist)
+    print(pathlist)
+    return pathlist
+
+def ConfigureJNI(env):
+    """Configure the given environment for compiling Java Native Interface
+       c or c++ language files."""
+    
+    print( "Configuring JNI includes")
+
+    if not env.get('JAVAC'):
+        print( "The Java compiler must be installed and in the current path.")
+        return 0
+
+    # first look for a shell variable called JAVA_HOME
+    java_base = os.environ.get('JAVA_HOME')
+    if not java_base:
+        if sys.platform == 'darwin':
+            # Apple's OS X has its own special java base directory
+            java_base = '/System/Library/Frameworks/JavaVM.framework'
+        else:
+            # Search for the java compiler
+            print ("JAVA_HOME environment variable is not set. Searching for java... ")
+            jcdir = os.path.dirname(env.WhereIs('javac'))
+            if not jcdir:
+                print( "not found.")
+                return 0
+            # assuming the compiler found is in some directory like
+            # /usr/jdkX.X/bin/javac, java's home directory is /usr/jdkX.X
+            java_base = os.path.join(jcdir, "..")
+            print( "found.")
+
+    if sys.platform == 'cygwin':
+        # Cygwin and Sun Java have different ideas of how path names
+        # are defined. Use cygpath to convert the windows path to
+        # a cygwin path. i.e. C:\jdkX.X to /cygdrive/c/jdkX.X
+        java_base = os.popen("cygpath -up '"+java_base+"'").read().replace( \
+                 '\n', '')
+
+    if sys.platform == 'darwin':
+        # Apple does not use Sun's naming convention
+        java_headers = [os.path.join(java_base, 'Headers')]
+        java_libs = [os.path.join(java_base, 'Libraries')]
+    else:
+        # windows and linux
+        java_headers = [os.path.join(java_base, 'include')]
+        java_libs = [os.path.join(java_base, 'lib')]
+        # Sun's windows and linux JDKs keep system-specific header
+        # files in a sub-directory of include
+        if java_base == '/usr' or java_base == '/usr/local':
+            # too many possible subdirectories. Just use defaults
+            java_headers.append(os.path.join(java_headers[0], 'win32'))
+            java_headers.append(os.path.join(java_headers[0], 'linux'))
+            java_headers.append(os.path.join(java_headers[0], 'solaris'))
+        else:
+            # add all subdirs of 'include'. The system specific headers
+            # should be in there somewhere
+            java_headers = walkDirs(java_headers[0])
+
+    if not any(os.path.exists(os.path.join(path, 'jni.h'))
+               for path in java_headers):
+        print("Can't find jni.h in %s" % java_headers)
+        return 0
+
+    # add Java's include and lib directory to the environment
+    java_headers.append(os.path.join(java_headers[0], 'linux'))
+    env.Append(CPPPATH = java_headers)
+    env.Append(LIBPATH = java_libs)
+
+    # add any special platform-specific compilation or linking flags
+    if sys.platform == 'darwin':
+        env.Append(SHLINKFLAGS = '-dynamiclib -framework JavaVM')
+        env['SHLIBSUFFIX'] = '.jnilib'
+    elif sys.platform == 'cygwin':
+        env.Append(CCFLAGS = '-mno-cygwin')
+        env.Append(SHLINKFLAGS = '-mno-cygwin -Wl,--kill-at')
+
+    # Add extra potentially useful environment variables
+    env['JAVA_HOME'] = java_base
+    env['JNI_CPPPATH'] = java_headers
+    env['JNI_LIBPATH'] = java_libs
+    return 1
\ No newline at end of file
diff --git a/jni/Example.java b/src/bindings/jni/Example.java
similarity index 75%
rename from jni/Example.java
rename to src/bindings/jni/Example.java
index 4e6d76825aa64ddeb9e916ea0596e457ff2751f0..52f89e27b42b1ee0f28658a5eba9df3a08674749 100644
--- a/jni/Example.java
+++ b/src/bindings/jni/Example.java
@@ -84,6 +84,20 @@ public static void main(String args[])
     Parser i3parsers[] = {Hammer.ch((byte)'i'), Hammer.uInt8(), Hammer.int8()};
     handle(Hammer.parse(Hammer.sequence(Hammer.ch((byte)'i'), Hammer.uInt8(), Hammer.int8()), i3, i3.length));
 
+    out("permutation");
+    byte ch3[] = {(byte) 'a', (byte) 'b', (byte) 'c'};
+    handle(Hammer.parse(Hammer.permutation(Hammer.ch((byte)'a'), Hammer.ch((byte)'b'), Hammer.ch((byte)'c')), ch3, ch3.length));
+    handle(Hammer.parse(Hammer.permutation(Hammer.ch((byte)'b'), Hammer.ch((byte)'a'), Hammer.ch((byte)'c')), ch3, ch3.length));
+    
+    out("skip");
+    byte ch6[] = {(byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f'};
+    handle(Hammer.parse(Hammer.sequence(Hammer.ch((byte)'a'), Hammer.skip((int)32), Hammer.ch((byte)'f')), ch6, ch6.length));
+    
+    out("seek");
+    final int SEEK_SET = 0;	/* Seek from beginning of file.  */
+    //final int SEEK_CUR = 1;	/* Seek from current position.  */
+    //final int SEEK_END = 2;	/* Seek from end of file.  */
+    handle(Hammer.parse(Hammer.sequence(Hammer.ch((byte)'a'), Hammer.seek((int)40, (int)SEEK_SET), Hammer.ch((byte)'f')), ch6, ch6.length));
     
 }
 
diff --git a/jni/Makefile b/src/bindings/jni/Makefile
similarity index 87%
rename from jni/Makefile
rename to src/bindings/jni/Makefile
index 85be973388223c6b9332c0d72a54f8a283e2c899..ea257872242da2e27c70eef26825137d4247d154 100644
--- a/jni/Makefile
+++ b/src/bindings/jni/Makefile
@@ -5,17 +5,18 @@ CSOURCES := com_upstandinghackers_hammer_Hammer.c com_upstandinghackers_hammer_P
 # ls *.h *.o *.so com/upstandinghackers/hammer/*.class | grep -v jhammer.h | tr '\n' ' '; replace single $ with $$
 OUTPUTS := com/upstandinghackers/hammer/Action.class com/upstandinghackers/hammer/Hammer.class com_upstandinghackers_hammer_Hammer.h com_upstandinghackers_hammer_Hammer.o com/upstandinghackers/hammer/Hammer\$TokenType.class com_upstandinghackers_hammer_Hammer_TokenType.h com/upstandinghackers/hammer/ParsedToken.class com_upstandinghackers_hammer_ParsedToken.h com_upstandinghackers_hammer_ParsedToken.o com/upstandinghackers/hammer/Parser.class com/upstandinghackers/hammer/ParseResult.class com_upstandinghackers_hammer_ParseResult.h com_upstandinghackers_hammer_ParseResult.o com_upstandinghackers_hammer_Parser.h com_upstandinghackers_hammer_Parser.o com/upstandinghackers/hammer/Predicate.class libjhammer.so
 
-TOPLEVEL := ../
+TOPLEVEL := ../../../
 
 JC=javac
 JH=javah
 CP=com/upstandinghackers/hammer
 PACKAGE=com.upstandinghackers.hammer
 
-include ../common.mk
+include ../../../common.mk
 
-JNI_INCLUDE := /usr/lib/jvm/java-6-openjdk/include/
-CFLAGS += -fPIC -I. -I $(TOPLEVEL)/src/ -I jni -I $(JNI_INCLUDE) 
+JNI_INCLUDE := /usr/lib/jvm/java-8-oracle/include/
+JNI_INCLUDE_LINUX := /usr/lib/jvm/java-8-oracle/include/linux
+CFLAGS += -fPIC -I. -I $(TOPLEVEL)/src/ -I jni -I $(JNI_INCLUDE) -I $(JNI_INCLUDE_LINUX)
 
 %.java: $(call ifsilent,| $(HUSH))
 	$(call hush, "Compiling Java source $@") $(JC) $(CP)/$@
@@ -23,7 +24,7 @@ CFLAGS += -fPIC -I. -I $(TOPLEVEL)/src/ -I jni -I $(JNI_INCLUDE)
 all: javacc prepare compile link
 
 link: compile
-	$(call hush, "Generating libjhammer.so") $(CC) -shared $(CFLAGS) -o libjhammer.so *.o ../src/*.o ../src/backends/*.o ../src/parsers/*.o
+	$(call hush, "Generating libjhammer.so") $(CC) -shared $(CFLAGS) -o libjhammer.so *.o ../../../src/*.o ../../../src/backends/*.o ../../../src/parsers/*.o
 
 $(CSOURCES): prepare
 	$(call hush, "Compiling $@") $(CC) -c $(CFLAGS) $@
diff --git a/jni/NOTES b/src/bindings/jni/NOTES
similarity index 100%
rename from jni/NOTES
rename to src/bindings/jni/NOTES
diff --git a/src/bindings/jni/SConscript b/src/bindings/jni/SConscript
new file mode 100644
index 0000000000000000000000000000000000000000..81582868ff07bff5d46d162a9d8ed2936dd52e28
--- /dev/null
+++ b/src/bindings/jni/SConscript
@@ -0,0 +1,42 @@
+# -*- python -*-
+
+from __future__ import absolute_import, division, print_function
+
+import os, os.path
+import sys
+Import('env libhammer_shared testruns targets')
+from src.bindings.jni.ConfigureJNI import ConfigureJNI
+
+javaenv = env.Clone()
+
+if not ConfigureJNI(javaenv):
+    print("Java Native Interface is required... Exiting")
+    Exit(0)
+
+
+javaenv.Append(CPPPATH=[".", "../.."],
+              LIBS=['hammer'],
+              LIBPATH=["../.."])
+
+# compile java classes
+jni_classes = javaenv.Java(".", "#src/bindings/jni/com")
+
+print(jni_classes)
+jni_headers = javaenv.JavaH(".", jni_classes)
+print(jni_headers)
+Default(jni_classes)
+Default(jni_headers)
+
+#print(javaenv.Dump())
+
+shlib_env = env.Clone(CPPPATH=javaenv['JNI_CPPPATH'] + ['../..'],
+              LIBS=['hammer'],
+              LIBPATH=["../.."])
+csources = ['com_upstandinghackers_hammer_Hammer.c',
+			'com_upstandinghackers_hammer_ParsedToken.c',
+			'com_upstandinghackers_hammer_Parser.c',
+			'com_upstandinghackers_hammer_ParseResult.c']
+
+libjhammer_shared = shlib_env.SharedLibrary('libjhammer', csources)
+Default(libjhammer_shared)
+
diff --git a/jni/com/upstandinghackers/hammer/Action.java b/src/bindings/jni/com/upstandinghackers/hammer/Action.java
similarity index 100%
rename from jni/com/upstandinghackers/hammer/Action.java
rename to src/bindings/jni/com/upstandinghackers/hammer/Action.java
diff --git a/jni/com/upstandinghackers/hammer/Hammer.java b/src/bindings/jni/com/upstandinghackers/hammer/Hammer.java
similarity index 85%
rename from jni/com/upstandinghackers/hammer/Hammer.java
rename to src/bindings/jni/com/upstandinghackers/hammer/Hammer.java
index 3e06a91eca4b15eebbfe28c99547829a3c2d6068..2b77a564af480140a21b8d2aa992c49d6bf2bcf4 100644
--- a/jni/com/upstandinghackers/hammer/Hammer.java
+++ b/src/bindings/jni/com/upstandinghackers/hammer/Hammer.java
@@ -53,10 +53,12 @@ public class Hammer
     public static native Parser middle(Parser p, Parser x, Parser q);
 //    public static native Parser action(Parser p, Action a);
     public static native Parser in(byte[] charset, int length);
+    public static native Parser notIn(byte[] charset, int length);
     public static native Parser endP();
     public static native Parser nothingP();
     public static native Parser sequence(Parser... parsers);
     public static native Parser choice(Parser... parsers);
+    public static native Parser permutation(Parser... parsers);
     public static native Parser butNot(Parser p1, Parser p2);
     public static native Parser difference(Parser p1, Parser p2);
     public static native Parser xor(Parser p1, Parser p2);
@@ -73,4 +75,11 @@ public class Hammer
     public static native Parser and(Parser p);
     public static native Parser not(Parser p);
     public static native Parser indirect();
+//    public static native Parser bindIndirect(Parser indirect, Parser inner);
+    public static native Parser withEndianess(byte endianess, Parser p);
+//    public static native Parser bind(Parser p, HContinuation k, void *env);
+    public static native Parser skip(int n);
+    public static native Parser seek(int n, int whence);
+    public static native Parser tell();
+    
 }
diff --git a/jni/com/upstandinghackers/hammer/ParseResult.java b/src/bindings/jni/com/upstandinghackers/hammer/ParseResult.java
similarity index 100%
rename from jni/com/upstandinghackers/hammer/ParseResult.java
rename to src/bindings/jni/com/upstandinghackers/hammer/ParseResult.java
diff --git a/jni/com/upstandinghackers/hammer/ParsedToken.java b/src/bindings/jni/com/upstandinghackers/hammer/ParsedToken.java
similarity index 100%
rename from jni/com/upstandinghackers/hammer/ParsedToken.java
rename to src/bindings/jni/com/upstandinghackers/hammer/ParsedToken.java
diff --git a/jni/com/upstandinghackers/hammer/Parser.java b/src/bindings/jni/com/upstandinghackers/hammer/Parser.java
similarity index 100%
rename from jni/com/upstandinghackers/hammer/Parser.java
rename to src/bindings/jni/com/upstandinghackers/hammer/Parser.java
diff --git a/jni/com/upstandinghackers/hammer/Predicate.java b/src/bindings/jni/com/upstandinghackers/hammer/Predicate.java
similarity index 100%
rename from jni/com/upstandinghackers/hammer/Predicate.java
rename to src/bindings/jni/com/upstandinghackers/hammer/Predicate.java
diff --git a/jni/com_upstandinghackers_hammer_Hammer.c b/src/bindings/jni/com_upstandinghackers_hammer_Hammer.c
similarity index 83%
rename from jni/com_upstandinghackers_hammer_Hammer.c
rename to src/bindings/jni/com_upstandinghackers_hammer_Hammer.c
index f83414bc8bd9c443a338e16300f4eb3bd3637d37..7087792789092167c4c3a1adcfa4487bbaf01a5e 100644
--- a/jni/com_upstandinghackers_hammer_Hammer.c
+++ b/src/bindings/jni/com_upstandinghackers_hammer_Hammer.c
@@ -157,6 +157,13 @@ JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_in
 }
 
 
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_notIn
+  (JNIEnv *env, jclass class, jbyteArray charset, jint length)
+{
+    RETURNWRAP(env, h_not_in((uint8_t*) ((*env)->GetByteArrayElements(env, charset, NULL)), (size_t)length));
+}
+
+
 JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_endP
   (JNIEnv *env, jclass class)
 {
@@ -227,6 +234,34 @@ JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_choice
 }
 
 
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_permutation
+  (JNIEnv *env, jclass class, jobjectArray permutation)
+{
+    jsize length;
+    void **parsers;
+    int i;
+    jobject current;
+    const HParser *result;
+
+    length = (*env)->GetArrayLength(env, permutation);
+    parsers = malloc(sizeof(HParser *)*(length+1));
+    if(NULL==parsers)
+    {
+        return NULL;
+    }
+
+    for(i=0; i<length; i++)
+    {
+        current = (*env)->GetObjectArrayElement(env, permutation, (jsize)i);
+        parsers[i] = UNWRAP(env, current);
+    }
+    parsers[length] = NULL;
+
+    result = h_permutation__a(parsers);
+    RETURNWRAP(env, result);
+}
+
+
 JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_butNot
   (JNIEnv *env, jclass class, jobject p, jobject q)
 {
@@ -332,4 +367,29 @@ JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_indirect
 }
 
 
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_with_endianness
+  (JNIEnv *env, jclass class, jbyte endianess, jobject p)
+{
+    RETURNWRAP(env, h_with_endianness((char) endianess, UNWRAP(env, p)));
+}
 
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_skip
+  (JNIEnv *env, jclass class, jint n)
+{
+    RETURNWRAP(env, h_skip((size_t) n));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_seek
+  (JNIEnv *env, jclass class, jint offset, jint whence)
+{
+    RETURNWRAP(env, h_seek((ssize_t) offset, (int) whence));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_tell
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_tell());
+}
diff --git a/jni/com_upstandinghackers_hammer_ParseResult.c b/src/bindings/jni/com_upstandinghackers_hammer_ParseResult.c
similarity index 100%
rename from jni/com_upstandinghackers_hammer_ParseResult.c
rename to src/bindings/jni/com_upstandinghackers_hammer_ParseResult.c
diff --git a/jni/com_upstandinghackers_hammer_ParsedToken.c b/src/bindings/jni/com_upstandinghackers_hammer_ParsedToken.c
similarity index 100%
rename from jni/com_upstandinghackers_hammer_ParsedToken.c
rename to src/bindings/jni/com_upstandinghackers_hammer_ParsedToken.c
diff --git a/jni/com_upstandinghackers_hammer_Parser.c b/src/bindings/jni/com_upstandinghackers_hammer_Parser.c
similarity index 100%
rename from jni/com_upstandinghackers_hammer_Parser.c
rename to src/bindings/jni/com_upstandinghackers_hammer_Parser.c
diff --git a/jni/jhammer.h b/src/bindings/jni/jhammer.h
similarity index 100%
rename from jni/jhammer.h
rename to src/bindings/jni/jhammer.h