in daffodil-core/src/main/scala/org/apache/daffodil/core/compiler/Compiler.scala [201:330]
def withValidateDFDLSchemas(value: Boolean): Compiler = copy(validateDFDLSchemas = value)
def withTunable(tunable: String, value: String): Compiler =
copy(tunables = tunables.withTunable(tunable, value))
def withTunables(tunablesArg: Map[String, String]): Compiler =
copy(tunables = tunables.withTunables(tunablesArg))
/**
* Controls whether we check everything in the schema, or just the element
* we care about (and everything reachable from it.)
*
* You need this control, since many of the big TDML test files have many things
* in them, some of which use unimplemented features. Each time we run exactly one
* test from the set, we want to ignore errors in compilation of the others.
*/
def withCheckAllTopLevel(flag: Boolean): Compiler =
copy(checkAllTopLevel = flag)
def reload(savedParser: File): DFDL.DataProcessor = reload(new FileInputStream(savedParser))
def reload(savedParser: java.nio.channels.ReadableByteChannel): DFDL.DataProcessor =
reload(Channels.newInputStream(savedParser))
def reload(schemaSource: DaffodilSchemaSource): DFDL.DataProcessor =
reload(schemaSource.uriForLoading)
def reload(uri: URI): DFDL.DataProcessor = reload(uri.toURL.openStream())
def reload(is: java.io.InputStream): DFDL.DataProcessor = {
try {
// Read the required prefix and version information for this saved parser
// directly from the input stream. This information is not compressed or
// serialized by a Java ObjectOutputStream
val requiredDataPrefix = "DAFFODIL "
requiredDataPrefix.foreach { c =>
if (is.read() != c.toInt)
throw new InvalidParserException(
"The saved parser is only compatible with an older version of Daffodil"
)
}
val ab = new ArrayBuffer[Byte]()
var byte = -1
while ({ byte = is.read(); byte > 0 }) {
ab.append(byte.toByte)
}
if (byte == -1) {
throw new InvalidParserException("The saved parser is corrupted")
}
val curVersion = Misc.getDaffodilVersion
val savedVersion = new String(ab.toArray, "utf-8")
if (savedVersion != curVersion) {
throw new InvalidParserException(
"The saved parser is only compatible with Daffodil " + savedVersion + ". Current version is " + curVersion
)
}
// Decompress and deserilize the rest of the file using java object deserializtion
val objInput = new ObjectInputStream(new GZIPInputStream(is)) {
///
/// This override is here because of a bug in sbt where the wrong class loader is being
/// used when deserializing an object.
// For more information, see https://github.com/sbt/sbt/issues/163
///
override def resolveClass(desc: java.io.ObjectStreamClass): Class[_] = {
try { Class.forName(desc.getName, false, getClass.getClassLoader) }
catch { case _: ClassNotFoundException => super.resolveClass(desc) }
}
}
val dpObj = objInput.readObject()
objInput.close()
val dp = dpObj.asInstanceOf[DataProcessor]
// must recompile the layers since their data structure in memory
// is not serializable
dp.ssrd.compileLayers()
dp
} catch {
case _: ZipException =>
throw new InvalidParserException("The saved parser is corrupted")
case _: StreamCorruptedException =>
throw new InvalidParserException("The saved parser is corrupted")
case ex: InvalidClassException =>
// This should only happen if users saves a schema with one version of
// dependency and tries to reload with a different version that is not
// serialization-compatible (e.g. save with scala 2.12.6 but reload
// with scala 2.12.11). We don't really know which versions of
// dependencies maintain serialization compatibility, and we it would
// be nice to allow users to upgrade libraries themselves for security
// purposes. So locking Daffodil to specific versions isn't ideal. If
// this exception is thrown, try to figure out which jar the
// incompatible class came from and point the user towards that to help
// figure out the issue.
val cls = Class.forName(ex.classname)
val src = cls.getProtectionDomain.getCodeSource
val dependencyStr =
if (src != null) (new File(src.getLocation.getFile)).getName else "a dependency"
throw new InvalidParserException(
"The saved parser was created with a different version of " + dependencyStr + " with incompatible class: " + ex.classname
)
//
case ex @ (_: ClassNotFoundException | _: NoClassDefFoundError |
_: InvalidObjectException | _: SchemaDefinitionError) =>
// These exception happens if a class that was used when saving
// is no longer on the classpath when reloading.
//
// One example of this happening is saving a schema using Java 8 but
// then reloading on Java 7, since some features (like base64 layers)
// rely on classes only available in Java 8.
//
// Another likely cause is if a user just has their classpath all wrong
// when reloading a schema, and dependencies are just missing, or if a
// user switches depenency versions and the new version completely
// removes a class.
//
// Another example is we use a special BitsCharsetSerializationProxy to
// serialize charsets. This proxy lets us do our own existence checks
// during deserialization and avoids very confusions and unhelpful
// messages from the default Java/Scala deserialization when the class
// does not exist. This custom logic throws an InvalidObjectException if
// the charset is not found
throw new InvalidParserException(
"The saved parser was created with a different set of dependencies containing a class no longer on the classpath: " + ex.getMessage
)
}
}