You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by sl...@apache.org on 2018/04/05 14:41:41 UTC

[incubator-daffodil] branch master updated: Add new tunable (errorOnUnsupportedJavaVersion) to allow running with older versions of Java

This is an automated email from the ASF dual-hosted git repository.

slawrence pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git


The following commit(s) were added to refs/heads/master by this push:
     new 3a5183a  Add new tunable (errorOnUnsupportedJavaVersion) to allow running with older versions of Java
3a5183a is described below

commit 3a5183a40a3df65219f68478fc8a662eab9cf3f4
Author: Steve Lawrence <sl...@apache.org>
AuthorDate: Tue Apr 3 08:50:21 2018 -0400

    Add new tunable (errorOnUnsupportedJavaVersion) to allow running with older versions of Java
    
    Currently we intentionally throw an exception if Daffodil is not run
    with Java 8 due to bugs in how Java 7 handles decoding errors. However,
    there are some cases where a user has no choice except to run under Java
    7.
    
    This patch adds a new tunable "errorOnUnsupportedJavaVersion" that causes
    Java 8 check failures to be logged as a warning rather than thrown as an
    exception. This allows the above use case to be possible, with the explicit
    understanding that it is not fully tested and is know to have unexpected
    behavior in some circumstances.
    
    Additionally, the file "/daffodil-config.xml" can be put on the
    classpath to override tunables globally, regardless of how Daffodil is
    run (e.g. CLI, TDMLRunner, API, etc.). This allows one to easily run all
    tests and processing with the errorOnUnsupportedJavaVersion check
    disable, or other tunables modified, if desired.
    
    DAFFODIL-1921
---
 .../org/apache/daffodil/compiler/Compiler.scala    | 19 +++++++++-
 .../apache/daffodil/util/CheckJavaVersion.scala    | 25 ++++++------
 .../org/apache/daffodil/api/DaffodilTunables.scala | 44 ++++++++++++++++++++--
 3 files changed, 71 insertions(+), 17 deletions(-)

diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/compiler/Compiler.scala b/daffodil-core/src/main/scala/org/apache/daffodil/compiler/Compiler.scala
index 9f39bbc..bc018ba 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/compiler/Compiler.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/compiler/Compiler.scala
@@ -39,6 +39,7 @@ import scala.collection.mutable.Queue
 import org.apache.daffodil.externalvars.ExternalVariablesLoader
 import org.apache.daffodil.processors.SchemaSetRuntimeData
 import org.apache.daffodil.util.CheckJavaVersion
+import org.apache.daffodil.util.InvalidJavaVersionException
 import org.apache.daffodil.api.ValidationMode
 import org.apache.daffodil.processors.VariableMap
 import java.util.zip.GZIPInputStream
@@ -162,7 +163,14 @@ final class ProcessorFactory(val sset: SchemaSet)
         rootERD,
         variables,
         validationMode)
-      CheckJavaVersion.checkJavaVersion(ssrd)
+      val versionErrorOpt = CheckJavaVersion.checkJavaVersion()
+      if (versionErrorOpt.isDefined) {
+        if (tunable.errorOnUnsupportedJavaVersion) {
+          throw new InvalidJavaVersionException(versionErrorOpt.get)
+        } else {
+          log(LogLevel.Warning, versionErrorOpt.get + " " + CheckJavaVersion.allowUnsupportedJavaMessage)
+        }
+      }
       val dataProc = new DataProcessor(ssrd)
       if (dataProc.isError) {
         // NO longer printing anything here. Callers must do this.
@@ -284,7 +292,14 @@ class Compiler(var validateDFDLSchemas: Boolean = true)
       val dpObj = objInput.readObject()
       objInput.close()
       val dp = dpObj.asInstanceOf[SerializableDataProcessor]
-      CheckJavaVersion.checkJavaVersion(dp.ssrd)
+      val versionErrorOpt = CheckJavaVersion.checkJavaVersion()
+      if (versionErrorOpt.isDefined) {
+        if (dp.getTunables.errorOnUnsupportedJavaVersion) {
+          throw new InvalidJavaVersionException(versionErrorOpt.get)
+        } else {
+          log(LogLevel.Warning, versionErrorOpt.get + " " + CheckJavaVersion.allowUnsupportedJavaMessage)
+        }
+      }
       dp
     } catch {
       case ex: ZipException => {
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/util/CheckJavaVersion.scala b/daffodil-core/src/main/scala/org/apache/daffodil/util/CheckJavaVersion.scala
index 0a93da1..1a8f1d6 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/util/CheckJavaVersion.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/util/CheckJavaVersion.scala
@@ -17,24 +17,27 @@
 
 package org.apache.daffodil.util
 
-import org.apache.daffodil.exceptions.ThrowsSDE
 import org.apache.daffodil.processors.charset.CharsetUtils
 
 class InvalidJavaVersionException(msg: String, cause: Throwable = null) extends Exception(msg, cause)
 
 object CheckJavaVersion {
 
-  def checkJavaVersion(context: ThrowsSDE) = {
+  def checkJavaVersion(): Option[String] = {
     val jVersion = scala.util.Properties.javaVersion
-    if (!scala.util.Properties.isJavaAtLeast("1.8")) {
-      throw new InvalidJavaVersionException("Daffodil requires Java 8 (1.8) or higher. You are currently running %s".format(jVersion))
-    }
-    //
-    // Test specifically for this particular decoder bug
-    // 
-    if (CharsetUtils.hasJava7DecoderBug) {
-      throw new InvalidJavaVersionException("This Java JVM has the Java 7 Decoder Bug. Daffodil requires Java 8 or higher.")
-    }
+    val errorStringOpt =
+      if (!scala.util.Properties.isJavaAtLeast("1.8")) {
+        Some("Daffodil requires Java 8 (1.8) or higher. You are currently running %s.".format(jVersion))
+      } else if (CharsetUtils.hasJava7DecoderBug) {
+        Some("This Java JVM has the Java 7 Decoder Bug. Daffodil requires Java 8 or higher.")
+      } else {
+        None
+      }
+    errorStringOpt
   }
 
+  val allowUnsupportedJavaMessage =
+    "Due to the tunable value of errorOnUnsupportedJavaVersion, " +
+    "processing will continue with the understanding that this is not " +
+    "fully tested and may have unexpected behavior in some circumstances."
 }
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/api/DaffodilTunables.scala b/daffodil-lib/src/main/scala/org/apache/daffodil/api/DaffodilTunables.scala
index c4850b0..27b090f 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/api/DaffodilTunables.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/api/DaffodilTunables.scala
@@ -21,18 +21,41 @@ import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.schema.annotation.props.gen.ParseUnparsePolicy
 import org.apache.daffodil.util.LogLevel
 import org.apache.daffodil.util.Logging
+import org.apache.daffodil.util.Misc
+import org.apache.daffodil.xml.DaffodilXMLLoader
 
 object DaffodilTunables {
 
   def apply(tunables: Map[String, String]): DaffodilTunables = {
-    new DaffodilTunables().setTunables(tunables)
+    apply().setTunables(tunables)
   }
 
   def apply(tunable: String, value: String): DaffodilTunables = {
-    new DaffodilTunables().setTunable(tunable, value)
+    apply().setTunable(tunable, value)
   }
 
-  def apply(): DaffodilTunables = new DaffodilTunables()
+  def apply(): DaffodilTunables = {
+    // override tunables from the global configuration file on the class path, if it exists
+    val (configOpt, _) = Misc.getResourceOption("/daffodil-config.xml")
+    val configTunables: Map[String, String] =
+      if (configOpt.isDefined) {
+        val loader = new DaffodilXMLLoader()
+        val node = loader.load(new URISchemaSource(configOpt.get))
+        val trimmed = scala.xml.Utility.trim(node)
+        val tunablesNode = (trimmed \ "tunables").headOption
+        val tunablesMap: Map[String, String] = tunablesNode match {
+          case None => Map.empty
+          case Some(tunableNode) => {
+            tunableNode.child.map { n => (n.label, n.text) }.toMap
+          }
+        }
+        tunablesMap
+      } else {
+        Map.empty
+      }
+
+    new DaffodilTunables().setTunables(configTunables)
+  }
 }
 
 case class DaffodilTunables(
@@ -105,7 +128,19 @@ case class DaffodilTunables(
   // This may cause a degredation of performance in path expression evaluation,
   // so this should be avoided when in production. This flag is automatically
   // enabled when debugging is enabled.
-  val allowExternalPathExpressions: Boolean = false)
+  val allowExternalPathExpressions: Boolean = false,
+
+  // A bug exists in Java 7 that causes unexpected behavior when decode errors
+  // occur in the specific ways that Daffodil decodes data. For this reason,
+  // Daffodil throws an exception when it detects that Daffodil is not running
+  // under Java 8 or has this decoder bug. However, there are some cases where
+  // a user has no choice but to run on Java 7. Setting this tunable to false
+  // will cause Daffodil to log a warning rather than throw an exception so
+  // that a user can run Daffodil on unsupported Java versions, with the
+  // understanding that it is not fully tested and behavior may not be well
+  // defined. This boolean is experimental and should only be used by those
+  // that fully understand the risks.
+  val errorOnUnsupportedJavaVersion: Boolean = true)
   extends Serializable
   with Logging
   with DataStreamLimits {
@@ -206,6 +241,7 @@ case class DaffodilTunables(
         this.copy(suppressSchemaDefinitionWarnings = warningsList)
       }
       case "allowexternalpathexpressions" => this.copy(allowExternalPathExpressions = java.lang.Boolean.valueOf(value))
+      case "erroronunsupportedjavaversion" => this.copy(errorOnUnsupportedJavaVersion = java.lang.Boolean.valueOf(value))
       case _ => {
         log(LogLevel.Warning, "Ignoring unknown tunable: %s", tunable)
         this

-- 
To stop receiving notification emails like this one, please contact
slawrence@apache.org.