diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9440c712378a4a0d678d63884198a18463cc9a29..741c90db885865a4b0e9198faa52653b62123690 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## 1.4.3
 
+* Improve Aparapi native to enforce Kernel and Device max work group size limitations and provide query functions for clGetKernelWorkGroupInfo(...)
+
 ## 1.4.2
 
 * Fixed Potential JVM crash when using multi-dimensional arrays (> 1D)
diff --git a/Makefile.am b/Makefile.am
index 6f94b38d4a6f01e9fb8faba3d9c9073d18645bdc..b8e873c2a6d7d96b0cb0d09e2187c2510075e75d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,8 +1,8 @@
 #ACLOCAL_AMFLAGS="-I m4"
 AUTOMAKE_OPTIONS = foreign
-EXTRA_DIST = include src/cpp/CLHelper.h src/cpp/classtools.h src/cpp/invoke/JavaArgs.h src/cpp/invoke/OpenCLMem.h src/cpp/invoke/OpenCLKernel.h src/cpp/invoke/OpenCLJNI.h src/cpp/invoke/OpenCLArgDescriptor.h src/cpp/invoke/OpenCLProgram.h src/cpp/CLException.h src/cpp/JNIHelper.h src/cpp/Common.h src/cpp/runKernel/KernelArg.h src/cpp/runKernel/Range.h src/cpp/runKernel/ProfileInfo.h src/cpp/runKernel/AparapiBuffer.h src/cpp/runKernel/Config.h src/cpp/runKernel/Aparapi.h src/cpp/runKernel/ArrayBuffer.h src/cpp/runKernel/JNIContext.h src/cpp/runKernel/List.h
+EXTRA_DIST = include src/cpp/CLHelper.h src/cpp/classtools.h src/cpp/invoke/JavaArgs.h src/cpp/invoke/OpenCLMem.h src/cpp/invoke/OpenCLKernel.h src/cpp/invoke/OpenCLJNI.h src/cpp/invoke/OpenCLArgDescriptor.h src/cpp/invoke/OpenCLProgram.h src/cpp/CLException.h src/cpp/JNIExceptions.h src/cpp/JNIHelper.h src/cpp/Common.h src/cpp/runKernel/KernelArg.h src/cpp/runKernel/Range.h src/cpp/runKernel/ProfileInfo.h src/cpp/runKernel/AparapiBuffer.h src/cpp/runKernel/Config.h src/cpp/runKernel/Aparapi.h src/cpp/runKernel/ArrayBuffer.h src/cpp/runKernel/JNIContext.h src/cpp/runKernel/List.h
 lib_LTLIBRARIES = libaparapi.la
 libaparapi_la_CPPFLAGS = $(AC_CPPFLAGS)
 libaparapi_la_LDFLAGS = $(AC_LDFLAGS)
-libaparapi_la_SOURCES = src/cpp/runKernel/Aparapi.cpp src/cpp/runKernel/ArrayBuffer.cpp src/cpp/runKernel/AparapiBuffer.cpp src/cpp/runKernel/Config.cpp src/cpp/runKernel/JNIContext.cpp src/cpp/runKernel/KernelArg.cpp src/cpp/runKernel/ProfileInfo.cpp src/cpp/runKernel/Range.cpp src/cpp/invoke/OpenCLJNI.cpp src/cpp/invoke/OpenCLArgDescriptor.cpp src/cpp/invoke/OpenCLMem.cpp src/cpp/CLHelper.cpp src/cpp/classtools.cpp src/cpp/JNIHelper.cpp src/cpp/agent.cpp
+libaparapi_la_SOURCES = src/cpp/runKernel/Aparapi.cpp src/cpp/runKernel/ArrayBuffer.cpp src/cpp/runKernel/AparapiBuffer.cpp src/cpp/runKernel/Config.cpp src/cpp/runKernel/JNIContext.cpp src/cpp/runKernel/KernelArg.cpp src/cpp/runKernel/ProfileInfo.cpp src/cpp/runKernel/Range.cpp src/cpp/invoke/OpenCLJNI.cpp src/cpp/invoke/OpenCLArgDescriptor.cpp src/cpp/invoke/OpenCLMem.cpp src/cpp/CLHelper.cpp src/cpp/classtools.cpp src/cpp/JNIHelper.cpp src/cpp/agent.cpp src/cpp/JNIExceptions.cpp
 all-local:
diff --git a/build.sh b/build.sh
index 11c26f96ff33cbf40584e9d01a596124d2ac3e48..198236ee7ebed674330a5abdc7b959e75c7d65c9 100755
--- a/build.sh
+++ b/build.sh
@@ -1,4 +1,5 @@
 #!/bin/sh
+rm -rf .libs64 .libs32
 make clean
 ./prepare.sh
 libtoolize
diff --git a/src/cpp/JNIExceptions.cpp b/src/cpp/JNIExceptions.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9df45bde6556c54cb0974af249a7bdaeeb8151a6
--- /dev/null
+++ b/src/cpp/JNIExceptions.cpp
@@ -0,0 +1,32 @@
+#include "JNIExceptions.h"
+
+jint throwNoClassDefError( JNIEnv *env, char *message )
+{
+    jclass exClass;
+    char *className = "java/lang/NoClassDefFoundError";
+
+    exClass = env->FindClass( className);
+    if (exClass == NULL) {
+       //Make JVM crash...
+       return throwNoClassDefError( env, className );
+    }
+
+    return env->ThrowNew( exClass, message );
+}
+
+jint throwAparapiJNIRuntimeException( JNIEnv *env, std::string message ) {
+   return throwAparapiJNIRuntimeException(env, (const char *)message.c_str());
+}
+
+jint throwAparapiJNIRuntimeException( JNIEnv *env, const char *message )
+{
+    jclass exClass;
+    char *className = "com/aparapi/exception/AparapiJNIException";
+
+    exClass = env->FindClass( className );
+    if (exClass == NULL) {
+        return throwNoClassDefError( env, className );
+    }
+
+    return env->ThrowNew( exClass, message );
+}
diff --git a/src/cpp/JNIExceptions.h b/src/cpp/JNIExceptions.h
new file mode 100644
index 0000000000000000000000000000000000000000..bcb44a076a122f1491d109e81870faf22cfd330a
--- /dev/null
+++ b/src/cpp/JNIExceptions.h
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2016 - 2018 Syncleus, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <jni.h>
+#include <string> 
+#include <stdio.h>
+#include <exception>
+#include "CLHelper.h"
+ 
+#ifndef JNI_EXCEPTIONS_H
+#define JNI_EXCEPTIONS_H
+
+class JNIException : public std::exception {
+
+private:
+   std::string _message;
+
+public:
+
+   ~JNIException() throw () {
+   }
+
+   JNIException(std::string message) {      
+      _message = message;
+   }
+
+   JNIException(const JNIException& cle) {
+      _message = cle._message;
+   }
+
+   JNIException& operator=(const JNIException& cle) {
+      _message = cle._message;
+      return *this;
+   }
+
+   const char* message() {
+      return _message.c_str();
+   }
+
+   void printError() {
+      if(_message != "") {
+         fprintf(stderr, "!!!!!!! %s failed %s\n", message());
+      }
+   }
+
+   const char* what() {
+       return std::string("!!!!!!! " + _message + " failed\n").c_str();
+   }
+};
+
+
+jint throwAparapiJNIRuntimeException( JNIEnv *env, std::string message );
+
+jint throwAparapiJNIRuntimeException( JNIEnv *env, const char *message );
+
+#endif // JNI_EXCEPTIONS_H
diff --git a/src/cpp/runKernel/Aparapi.cpp b/src/cpp/runKernel/Aparapi.cpp
index 807bf81e437612ae98e6158282c4b1e34c488c34..73d5bc1c250cfd5f2c425755562c840dd1284326 100644
--- a/src/cpp/runKernel/Aparapi.cpp
+++ b/src/cpp/runKernel/Aparapi.cpp
@@ -61,6 +61,7 @@
 #include "ProfileInfo.h"
 #include "ArrayBuffer.h"
 #include "AparapiBuffer.h"
+#include "JNIExceptions.h"
 #include "CLHelper.h"
 #include "List.h"
 #include "Util.h"
@@ -843,6 +844,238 @@ int processArgs(JNIEnv* jenv, JNIContext* jniContext, int& argPos, int& writeEve
    return status;
 }
 
+JNI_JAVA(jlong, KernelRunnerJNI, getKernelMinimumPrivateMemSizeInUsePerWorkItemJNI)
+   (JNIEnv *jEnv, jobject jobj, jlong jniContextHandle) {
+   long maxPrivateMemSize = -1;
+   
+   try {   
+      JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+      if (jniContext == NULL){
+         throwAparapiJNIRuntimeException(jEnv, "Failed to obtain JNI context from handle");
+         throw JNIException("getJNIContext()");
+      }
+
+      int length = queryKernelWorkGroupInfo(jEnv, jniContext, CL_KERNEL_PRIVATE_MEM_SIZE, &maxPrivateMemSize);
+      if (length != 1) {
+         throwAparapiJNIRuntimeException(jEnv, "getKernelMinimumPrivateMemSizeInUsePerWorkItemJNI() invalid length" + std::to_string(length));
+         throw JNIException("queryKernelWorkGroupInfo() invalid length: " + length);
+      }
+
+      return (jlong)maxPrivateMemSize;	
+   } catch (JNIException &ex) {
+      //Log or ignore on puropose, as a JNI exception is on its way
+   } catch (CLException &ex) {
+      //Log or ignore on puropose, as a JNI exception is on its way
+   }
+   
+   //This value will never be returned, because a JNI exception is on its way
+   return 0; 
+}
+
+JNI_JAVA(jlong, KernelRunnerJNI, getKernelLocalMemSizeInUseJNI)
+      (JNIEnv *jEnv, jobject jobj, jlong jniContextHandle) {
+   long maxLocalMemSize = -1;
+
+   try {   
+      JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+      if (jniContext == NULL){
+         throwAparapiJNIRuntimeException(jEnv, "Failed to obtain JNI context from handle");
+         throw JNIException("getJNIContext()");
+      }
+
+      int length = queryKernelWorkGroupInfo(jEnv, jniContext, CL_KERNEL_LOCAL_MEM_SIZE, &maxLocalMemSize);
+      if (length != 1) {
+         throwAparapiJNIRuntimeException(jEnv, "getKernelLocalMemSizeInUseJNI() invalid length" + std::to_string(length));
+         throw JNIException("queryKernelWorkGroupInfo() invalid length: " + length);
+      }
+      
+      return (jlong)maxLocalMemSize;
+   } catch (JNIException &ex) {
+      //Log or ignore on puropose, as a JNI exception is on its way
+   } catch (CLException &ex) {
+      //Log or ignore on puropose, as a JNI exception is on its way
+   }
+   
+   //This value will never be returned, because a JNI exception is on its way
+   return 0; 
+}
+
+JNI_JAVA(jint, KernelRunnerJNI, getKernelPreferredWorkGroupSizeMultipleJNI)
+      (JNIEnv *jEnv, jobject jobj, jlong jniContextHandle) {
+   long preferredWorkGroupSize[3];
+
+   try {   
+      JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+      if (jniContext == NULL){
+         throwAparapiJNIRuntimeException(jEnv, "Failed to obtain JNI context from handle");
+         throw JNIException("getJNIContext()");
+      }
+
+      int length = queryKernelWorkGroupInfo(jEnv, jniContext, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, preferredWorkGroupSize);
+      if (length != 1) {
+         throwAparapiJNIRuntimeException(jEnv, "getKernelPreferredWorkGroupSizeMultipleJNI() invalid length" + std::to_string(length));
+         throw JNIException("queryKernelWorkGroupInfo() invalid length: " + length);
+      }
+   
+      return (jint)preferredWorkGroupSize[0];
+   } catch (JNIException &ex) {
+      //Log or ignore on puropose, as a JNI exception is on its way
+   } catch (CLException &ex) {
+      //Log or ignore on puropose, as a JNI exception is on its way
+   }
+   
+   //This value will never be returned, because a JNI exception is on its way
+   return 0;
+}
+
+JNI_JAVA(jint, KernelRunnerJNI, getKernelMaxWorkGroupSizeJNI)
+      (JNIEnv *jEnv, jobject jobj, jlong jniContextHandle) {
+   long maxWorkGroupSize[3];
+
+   try {   
+      JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+      if (jniContext == NULL){
+         throwAparapiJNIRuntimeException(jEnv, "Failed to obtain JNI context from handle");
+         throw JNIException("getJNIContext()");
+      }
+   
+      int length = queryKernelWorkGroupInfo(jEnv, jniContext, CL_KERNEL_WORK_GROUP_SIZE, maxWorkGroupSize);
+      if (length != 1) {
+         throwAparapiJNIRuntimeException(jEnv, "getKernelMaxWorkGroupSizeJNI() invalid length" + std::to_string(length));
+         throw JNIException("queryKernelWorkGroupInfo() invalid length: " + length);
+      }
+   
+      return (jint)maxWorkGroupSize[0];
+   } catch (JNIException &ex) {
+      //Log or ignore on puropose, as a JNI exception is on its way
+   } catch (CLException &ex) {
+      //Log or ignore on puropose, as a JNI exception is on its way
+   }
+   
+   //This value will never be returned, because a JNI exception is on its way
+   return 0;
+}
+
+JNI_JAVA(jintArray, KernelRunnerJNI, getKernelCompileWorkGroupSizeJNI)
+   (JNIEnv *jEnv, jobject jobj, jlong jniContextHandle) {
+   long compileWorkGroupSize[3];
+   
+   try {
+      JNIContext* jniContext = JNIContext::getJNIContext(jniContextHandle);
+      if (jniContext == NULL){
+         throwAparapiJNIRuntimeException(jEnv, "Failed to obtain JNI context from handle");
+         throw JNIException("getJNIContext()");
+      }
+
+      int length = queryKernelWorkGroupInfo(jEnv, jniContext, CL_KERNEL_COMPILE_WORK_GROUP_SIZE, compileWorkGroupSize);
+      if (length == 0) {
+         throwAparapiJNIRuntimeException(jEnv, "getKernelCompileWorkGroupSizeJNI() invalid length: " + std::to_string(length));
+         throw JNIException("queryKernelWorkGroupInfo() invalid length: " + length);
+      } 
+      
+      jintArray jArr = jEnv->NewIntArray(length);
+      if (jArr == NULL) {
+         throwAparapiJNIRuntimeException(jEnv, "Failed to create Java integer array - NewIntArray()");
+         throw JNIException("Failed to create Java integer array - NewIntArray()");
+      }
+         
+      int arr[3];
+      for (int i = 0; i < length; i++) {
+         arr[i] = (jint)compileWorkGroupSize[i];
+      }
+   
+      jEnv->SetIntArrayRegion(jArr, (jsize)0, (jsize)length, arr);
+   
+      return jArr;
+   } catch (JNIException &ex) {
+      //Log or ignore on puropose, as a JNI exception is on its way
+   } catch (CLException& ex) {
+      //Log or ignore on puropose, as a JNI exception is on its way
+   }
+
+   //This value will never be returned, because a JNI exception is on its way
+   return NULL;
+}
+
+/**
+ * queries OpenCL about a compiled Kernel that is intended to run on a specific device
+ *
+ * @param jniContext the context with the arguments
+ * @param queryParameter the openCL parameter that is to be queried
+ * @param result a reference to an array of long with size 3 of the result values
+ * @return the number of entries with valid data in the array, 0 if there was an error
+ *
+ * @throws CLException
+ */
+int queryKernelWorkGroupInfo(JNIEnv *jEnv, JNIContext* jniContext, cl_kernel_work_group_info queryParameter, long *result) {
+   size_t queryResult[3];
+   cl_ulong queryResultULong;
+   
+   size_t returnedSize = 0;
+   void *voidQueryResult;
+   size_t voidQuerySize;
+   size_t unitSize;
+   
+   int resultLength = 0;
+   
+   switch (queryParameter) {
+   case CL_KERNEL_WORK_GROUP_SIZE:
+      voidQueryResult = (void *)queryResult;
+      voidQuerySize = sizeof(size_t);
+      unitSize = sizeof(size_t);
+      break;
+   case CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE:
+      voidQueryResult = (void *)queryResult;
+      voidQuerySize = sizeof(size_t);
+      unitSize = sizeof(size_t);
+      break;   
+   case CL_KERNEL_LOCAL_MEM_SIZE:
+      voidQueryResult = (void *)&queryResultULong;
+      voidQuerySize = sizeof(queryResultULong);
+      unitSize = sizeof(queryResultULong);
+      break;
+   case CL_KERNEL_PRIVATE_MEM_SIZE:
+      voidQueryResult = (void *)&queryResultULong;
+      voidQuerySize = sizeof(queryResultULong);
+      unitSize = sizeof(queryResultULong);
+      break;
+   default:
+      voidQueryResult = (void *)queryResult;
+      voidQuerySize = sizeof(queryResult);
+      unitSize = sizeof(size_t);
+      break;
+   }
+   
+   cl_int status = clGetKernelWorkGroupInfo(jniContext->kernel, (cl_device_id)jniContext->deviceId, queryParameter,
+                                            voidQuerySize, voidQueryResult, &returnedSize); 
+   if (status != CL_SUCCESS) {
+      if (jEnv != NULL) {
+         throwAparapiJNIRuntimeException(jEnv, "clGetKernelWorkGroupInfo() failed with error: " + std::string(CLHelper::errString(status)));
+      }
+      throw CLException(status, "clGetKernelWorkGroupInfo()");
+   }
+   
+   switch (queryParameter) {
+   case CL_KERNEL_PRIVATE_MEM_SIZE:
+      resultLength = 1;
+      *result = (long)queryResultULong;
+      break;
+   case CL_KERNEL_LOCAL_MEM_SIZE:
+      resultLength = 1;
+      *result = (long)queryResultULong;
+      break;
+   default:
+      resultLength = returnedSize / unitSize;
+      
+      for (int i = 0; i < resultLength; i++) {
+         result[i] = (long)queryResult[i];
+      }
+      break;
+   }
+   
+   return resultLength;
+}
+
 /**
  * enqueus the current kernel to run on opencl
  *
@@ -879,6 +1112,24 @@ void enqueueKernel(JNIContext* jniContext, Range& range, int passes, int argPos,
    jbyte* kernelInBytes = jniContext->runKernelInBytes;
    int* kernelInBytesAsInts = reinterpret_cast<int*>(kernelInBytes);
 
+
+   //Enforce validation of local work group size as some OpenCL vendor implementations don't do it
+   long maxKernelWorkGroupSize = 0;
+   int queryResults = queryKernelWorkGroupInfo(NULL, jniContext, CL_KERNEL_WORK_GROUP_SIZE, &maxKernelWorkGroupSize);
+   if (queryResults != 1) {
+      throw JNIException("Failed to retrieve MAX Kernel WorkGroup size: got " + std::to_string(queryResults) + " values instead of 1");
+   }
+
+   int targetWorkGroupSize = 1;
+   for (int i = 0; i < range.dims; i++) {
+      targetWorkGroupSize *= range.localDims[i];
+   }
+   
+   if (targetWorkGroupSize > maxKernelWorkGroupSize) {
+      throw JNIException("Kernel overall local size: " + std::to_string(targetWorkGroupSize) + 
+                        " exceeds maximum kernel allowed local size of: " + std::to_string(maxKernelWorkGroupSize));
+   }
+
    cl_int status = CL_SUCCESS;
    for (int passid=0; passid < passes; passid++) {
 
@@ -1219,7 +1470,11 @@ JNI_JAVA(jint, KernelRunnerJNI, runKernelJNI)
          jniContext->unpinAll(jenv);
          return cle.status();
       }
-
+      catch(JNIException& jnie) {
+         jnie.printError();
+         jniContext->unpinAll(jenv);
+         return -1;
+      }
 
 
 
diff --git a/src/cpp/runKernel/Aparapi.h b/src/cpp/runKernel/Aparapi.h
index 68b82bb1b55ce706b08a007933d7525bcaf9e24d..2321734243d3c716be07d0a98fc7e9af0ca22a4e 100644
--- a/src/cpp/runKernel/Aparapi.h
+++ b/src/cpp/runKernel/Aparapi.h
@@ -107,5 +107,6 @@ void writeProfile(JNIEnv* jenv, JNIContext* jniContext);
 
 KernelArg* getArgForBuffer(JNIEnv* jenv, JNIContext* jniContext, jobject buffer);
 
+int queryKernelWorkGroupInfo(JNIEnv *jEnv, JNIContext* jniContext, cl_kernel_work_group_info queryParameter, long *result);
 
 #endif // APARAPI_H