diff --git a/java/change-notes/2021-08-04-jabsorb-unsafe-deserialization.md b/java/change-notes/2021-08-04-jabsorb-unsafe-deserialization.md
new file mode 100644
index 00000000000..06cef68422e
--- /dev/null
+++ b/java/change-notes/2021-08-04-jabsorb-unsafe-deserialization.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The "Deserialization of user-controlled data" (`java/unsafe-deserialization`) query now recognizes deserialization using the `Jabsorb` library.
diff --git a/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp
index c580d36e2cc..9c87aae7e37 100644
--- a/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp
+++ b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp
@@ -15,7 +15,8 @@ may have unforeseen effects, such as the execution of arbitrary code.
There are many different serialization frameworks. This query currently
supports Kryo, XmlDecoder, XStream, SnakeYaml, JYaml, JsonIO, YAMLBeans, HessianBurlap, Castor, Burlap,
-Jackson and Java IO serialization through ObjectInputStream/ObjectOutputStream.
+Jackson, Jabsorb and Java IO serialization through
+ObjectInputStream/ObjectOutputStream.
@@ -100,6 +101,10 @@ Blog posts by the developer of Jackson libraries:
On Jackson CVEs: Don’t Panic — Here is what you need to know
Jackson 2.10: Safe Default Typing
+
+Jabsorb documentation on deserialization:
+Jabsorb JSON Serializer.
+
diff --git a/java/ql/src/semmle/code/java/frameworks/Jabsorb.qll b/java/ql/src/semmle/code/java/frameworks/Jabsorb.qll
new file mode 100644
index 00000000000..0255557f69d
--- /dev/null
+++ b/java/ql/src/semmle/code/java/frameworks/Jabsorb.qll
@@ -0,0 +1,26 @@
+/**
+ * Provides classes for working with the Jabsorb JSON-RPC ORB framework.
+ */
+
+import java
+
+/** The class `org.jabsorb.JSONSerializer`. */
+class JabsorbSerializer extends RefType {
+ JabsorbSerializer() { this.hasQualifiedName("org.jabsorb", "JSONSerializer") }
+}
+
+/** The deserialization method `unmarshall`. */
+class JabsorbUnmarshallMethod extends Method {
+ JabsorbUnmarshallMethod() {
+ this.getDeclaringType().getASupertype*() instanceof JabsorbSerializer and
+ this.getName() = "unmarshall"
+ }
+}
+
+/** The deserialization method `fromJSON`. */
+class JabsorbFromJsonMethod extends Method {
+ JabsorbFromJsonMethod() {
+ this.getDeclaringType().getASupertype*() instanceof JabsorbSerializer and
+ this.getName() = "fromJSON"
+ }
+}
diff --git a/java/ql/src/semmle/code/java/frameworks/Jackson.qll b/java/ql/src/semmle/code/java/frameworks/Jackson.qll
index 505a8e9cdf0..5612311c730 100644
--- a/java/ql/src/semmle/code/java/frameworks/Jackson.qll
+++ b/java/ql/src/semmle/code/java/frameworks/Jackson.qll
@@ -3,7 +3,6 @@
*/
import java
-private import semmle.code.java.Reflection
private import semmle.code.java.dataflow.DataFlow
private class ObjectMapper extends RefType {
@@ -77,14 +76,6 @@ class SetPolymorphicTypeValidatorSource extends DataFlow::ExprNode {
}
}
-/** Holds if `fromNode` to `toNode` is a dataflow step that resolves a class. */
-predicate resolveClassStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
- exists(ReflectiveClassIdentifierMethodAccess ma |
- ma.getArgument(0) = fromNode.asExpr() and
- ma = toNode.asExpr()
- )
-}
-
/**
* Holds if `fromNode` to `toNode` is a dataflow step that creates a Jackson parser.
*
@@ -153,22 +144,3 @@ predicate hasArgumentWithUnsafeJacksonAnnotation(MethodAccess call) {
hasJsonTypeInfoAnnotation(argType.(ParameterizedType).getATypeArgument())
)
}
-
-/**
- * Holds if `fromNode` to `toNode` is a dataflow step that looks like resolving a class.
- * A method probably resolves a class if it takes a string, returns a type descriptor,
- * and its name contains "resolve", "load", etc.
- *
- * Any method call that satisfies the rule above is assumed to propagate taint from its string arguments,
- * so methods that accept user-controlled data but sanitize it or use it for some
- * completely different purpose before returning a type descriptor could result in false positives.
- */
-predicate looksLikeResolveClassStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
- exists(MethodAccess ma, Method m, Expr arg | m = ma.getMethod() and arg = ma.getAnArgument() |
- m.getReturnType() instanceof JacksonTypeDescriptorType and
- m.getName().toLowerCase().regexpMatch("(.*)(resolve|load|class|type)(.*)") and
- arg.getType() instanceof TypeString and
- arg = fromNode.asExpr() and
- ma = toNode.asExpr()
- )
-}
diff --git a/java/ql/src/semmle/code/java/security/UnsafeDeserializationQuery.qll b/java/ql/src/semmle/code/java/security/UnsafeDeserializationQuery.qll
index b9cfa1ddde8..fe58166965c 100644
--- a/java/ql/src/semmle/code/java/security/UnsafeDeserializationQuery.qll
+++ b/java/ql/src/semmle/code/java/security/UnsafeDeserializationQuery.qll
@@ -14,6 +14,7 @@ private import semmle.code.java.frameworks.YamlBeans
private import semmle.code.java.frameworks.HessianBurlap
private import semmle.code.java.frameworks.Castor
private import semmle.code.java.frameworks.Jackson
+private import semmle.code.java.frameworks.Jabsorb
private import semmle.code.java.frameworks.apache.Lang
private import semmle.code.java.Reflection
@@ -184,6 +185,13 @@ predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
hasArgumentWithUnsafeJacksonAnnotation(ma)
) and
not exists(SafeObjectMapperConfig config | config.hasFlowToExpr(ma.getQualifier()))
+ or
+ m instanceof JabsorbUnmarshallMethod and
+ sink = ma.getArgument(2) and
+ any(UnsafeTypeConfig config).hasFlowToExpr(ma.getArgument(1))
+ or
+ m instanceof JabsorbFromJsonMethod and
+ sink = ma.getArgument(0)
)
}
@@ -243,9 +251,36 @@ class UnsafeDeserializationConfig extends TaintTracking::Configuration {
}
}
+/** Holds if `fromNode` to `toNode` is a dataflow step that resolves a class. */
+predicate resolveClassStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
+ exists(ReflectiveClassIdentifierMethodAccess ma |
+ ma.getArgument(0) = fromNode.asExpr() and
+ ma = toNode.asExpr()
+ )
+}
+
+/**
+ * Holds if `fromNode` to `toNode` is a dataflow step that looks like resolving a class.
+ * A method probably resolves a class if it takes a string, returns a type descriptor,
+ * and its name contains "resolve", "load", etc.
+ *
+ * Any method call that satisfies the rule above is assumed to propagate taint from its string arguments,
+ * so methods that accept user-controlled data but sanitize it or use it for some
+ * completely different purpose before returning a type descriptor could result in false positives.
+ */
+predicate looksLikeResolveClassStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
+ exists(MethodAccess ma, Method m, Expr arg | m = ma.getMethod() and arg = ma.getAnArgument() |
+ m.getReturnType() instanceof JacksonTypeDescriptorType and
+ m.getName().regexpMatch("(?i).*(resolve|load|class|type).*") and
+ arg.getType() instanceof TypeString and
+ arg = fromNode.asExpr() and
+ ma = toNode.asExpr()
+ )
+}
+
/**
* Tracks flow from a remote source to a type descriptor (e.g. a `java.lang.Class` instance)
- * passed to a Jackson deserialization method.
+ * passed to a deserialization method.
*
* If this is user-controlled, arbitrary code could be executed while instantiating the user-specified type.
*/
@@ -256,7 +291,12 @@ class UnsafeTypeConfig extends TaintTracking2::Configuration {
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma, int i, Expr arg | i > 0 and ma.getArgument(i) = arg |
- ma.getMethod() instanceof ObjectMapperReadMethod and
+ (
+ ma.getMethod() instanceof ObjectMapperReadMethod
+ or
+ ma.getMethod() instanceof JabsorbUnmarshallMethod
+ ) and
+ // Note `JacksonTypeDescriptorType` includes plain old `java.lang.Class`
arg.getType() instanceof JacksonTypeDescriptorType and
arg = sink.asExpr()
)
diff --git a/java/ql/test/query-tests/security/CWE-502/JabsorbServlet.java b/java/ql/test/query-tests/security/CWE-502/JabsorbServlet.java
new file mode 100644
index 00000000000..14e8d1819c6
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-502/JabsorbServlet.java
@@ -0,0 +1,121 @@
+import java.io.IOException;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+import org.jabsorb.JSONSerializer;
+import org.jabsorb.serializer.SerializerState;
+import org.jabsorb.serializer.ObjectMatch;
+
+import com.example.User;
+import com.thirdparty.Person;
+
+public class JabsorbServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ // GOOD: final class type specified
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String json = req.getParameter("json");
+ String clazz = req.getParameter("class");
+
+ try {
+ Object jsonObject = new JSONObject(json);
+
+ JSONSerializer serializer = new JSONSerializer();
+ serializer.registerDefaultSerializers();
+
+ serializer.setMarshallClassHints(true);
+ serializer.setMarshallNullAttributes(true);
+
+ SerializerState state = new SerializerState();
+ User user = (User) serializer.unmarshall(state, User.class, jsonObject);
+ } catch (Exception e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ // GOOD: concrete class type specified even if it has vulnerable subclasses
+ public void doHead(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String json = req.getParameter("json");
+ String clazz = req.getParameter("class");
+
+ try {
+ Object jsonObject = new JSONObject(json);
+
+ JSONSerializer serializer = new JSONSerializer();
+ serializer.registerDefaultSerializers();
+
+ serializer.setMarshallClassHints(true);
+ serializer.setMarshallNullAttributes(true);
+
+ SerializerState state = new SerializerState();
+ Person person = (Person) serializer.unmarshall(state, Person.class, jsonObject);
+ } catch (Exception e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ @Override
+ // GOOD: try unmarshall but doesn't actually marshall the object
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String json = req.getParameter("json");
+ String clazz = req.getParameter("class");
+
+ try {
+ Object jsonObject = new JSONObject(json);
+
+ JSONSerializer serializer = new JSONSerializer();
+ serializer.registerDefaultSerializers();
+
+ serializer.setMarshallClassHints(true);
+ serializer.setMarshallNullAttributes(true);
+
+ SerializerState state = new SerializerState();
+ ObjectMatch objMatch = serializer.tryUnmarshall(state, Class.forName(clazz), jsonObject);
+ User obj = new User();
+ boolean result = objMatch.equals(obj);
+ } catch (Exception e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ @Override
+ // BAD: allow class name to be controlled by remote source
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String json = req.getParameter("json");
+ String clazz = req.getParameter("class");
+
+ try {
+ Object jsonObject = new JSONObject(json);
+
+ JSONSerializer serializer = new JSONSerializer();
+ serializer.registerDefaultSerializers();
+
+ serializer.setMarshallClassHints(true);
+ serializer.setMarshallNullAttributes(true);
+
+ SerializerState state = new SerializerState();
+ User user = (User) serializer.unmarshall(state, Class.forName(clazz), jsonObject); // $unsafeDeserialization
+ } catch (Exception e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ // BAD: allow explicit class type controlled by remote source in the format of "json={\"javaClass\":\"com.thirdparty.Attacker\", ...}"
+ public void doPut2(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String json = req.getParameter("json");
+
+ try {
+ JSONSerializer serializer = new JSONSerializer();
+ serializer.registerDefaultSerializers();
+
+ User user = (User) serializer.fromJSON(json); // $unsafeDeserialization
+ } catch (Exception e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/query-tests/security/CWE-502/com/example/User.java b/java/ql/test/query-tests/security/CWE-502/com/example/User.java
new file mode 100644
index 00000000000..f89dc1c3872
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-502/com/example/User.java
@@ -0,0 +1,29 @@
+package com.example;
+
+public final class User {
+ private String uid;
+ private String name;
+
+ public User() {
+ }
+
+ public String getUid() {
+ return uid;
+ }
+
+ public void setUid(String uid) {
+ this.uid = uid;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return "User[ name = "+name+", uid: "+uid+ "]";
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-502/com/thirdparty/Person.java b/java/ql/test/query-tests/security/CWE-502/com/thirdparty/Person.java
new file mode 100644
index 00000000000..e152aab1cde
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-502/com/thirdparty/Person.java
@@ -0,0 +1,29 @@
+package com.thirdparty;
+
+public class Person {
+ private int snum;
+ private String name;
+
+ public Person() {
+ }
+
+ public int getSnum() {
+ return snum;
+ }
+
+ public void setSnum(int snum) {
+ this.snum = snum;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return "Person[ name = "+name+", snum: "+snum+ "]";
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-502/options b/java/ql/test/query-tests/security/CWE-502/options
index aedb1d0518b..e3f830b52fc 100644
--- a/java/ql/test/query-tests/security/CWE-502/options
+++ b/java/ql/test/query-tests/security/CWE-502/options
@@ -1 +1 @@
-//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/jabsorb-1.3.2:${testdir}/../../../stubs/json-java-20210307
diff --git a/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/JSONSerializer.java b/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/JSONSerializer.java
new file mode 100644
index 00000000000..931d0dccf0d
--- /dev/null
+++ b/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/JSONSerializer.java
@@ -0,0 +1,319 @@
+/*
+ * jabsorb - a Java to JavaScript Advanced Object Request Broker
+ * http://www.jabsorb.org
+ *
+ * Copyright 2007-2009 The jabsorb team
+ *
+ * based on original code from
+ * JSON-RPC-Java - a JSON-RPC to Java Bridge with dynamic invocation
+ *
+ * Copyright Metaparadigm Pte. Ltd. 2004.
+ * Michael Clark
+ *
+ * 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.
+ *
+ */
+
+package org.jabsorb;
+
+import java.io.Serializable;
+
+import org.jabsorb.serializer.MarshallException;
+import org.jabsorb.serializer.ObjectMatch;
+import org.jabsorb.serializer.Serializer;
+import org.jabsorb.serializer.SerializerState;
+import org.jabsorb.serializer.UnmarshallException;
+
+/**
+ * This class is the public entry point to the serialization code and provides
+ * methods for marshalling Java objects into JSON objects and unmarshalling JSON
+ * objects into Java objects.
+ */
+public class JSONSerializer implements Serializable
+{
+ /**
+ * Determine if this serializer considers the given Object to be a primitive
+ * wrapper type Object. This is used to determine which types of Objects
+ * should be fixed up as duplicates if the fixupDuplicatePrimitives flag
+ * is false.
+ *
+ * @param o Object to test for primitive.
+ */
+ public boolean isPrimitive(Object o)
+ {
+ return false;
+ }
+
+ /**
+ * Get the fixupCircRefs flag. If true, FixUps are generated to handle circular
+ * references found during marshalling. If false, an exception is thrown if a
+ * circular reference is found during serialization.
+ *
+ * @return the fixupCircRefs flag.
+ */
+ public boolean getFixupCircRefs()
+ {
+ return false;
+ }
+
+ /**
+ * Set the fixupCircRefs flag. If true, FixUps are generated to handle circular
+ * references found during marshalling. If false, an exception is thrown if a
+ * circular reference is found during serialization.
+ *
+ * @param fixupCircRefs the fixupCircRefs flag.
+ */
+ public void setFixupCircRefs(boolean fixupCircRefs)
+ {
+ }
+
+ /**
+ * Get the fixupDuplicates flag. If true, FixUps are generated for duplicate
+ * objects found during marshalling. If false, the duplicates are re-serialized.
+ *
+ * @return the fixupDuplicates flag.
+ */
+ public boolean getFixupDuplicates()
+ {
+ return false;
+ }
+
+ /**
+ * Set the fixupDuplicates flag. If true, FixUps are generated for duplicate
+ * objects found during marshalling. If false, the duplicates are re-serialized.
+ *
+ * @param fixupDuplicates the fixupDuplicates flag.
+ */
+ public void setFixupDuplicates(boolean fixupDuplicates)
+ {
+ }
+
+ /**
+ * Get the fixupDuplicatePrimitives flag. If true (and fixupDuplicates is
+ * also true), FixUps are generated for duplicate primitive objects found
+ * during marshalling. If false, the duplicates are re-serialized.
+ *
+ * @return the fixupDuplicatePrimitives flag.
+ */
+ public boolean getFixupDuplicatePrimitives()
+ {
+ return false;
+ }
+
+ /**
+ * Set the fixupDuplicatePrimitives flag. If true (and fixupDuplicates is
+ * also true), FixUps are generated for duplicate primitive objects found
+ * during marshalling. If false, the duplicates are re-serialized.
+ *
+ * @param fixupDuplicatePrimitives the fixupDuplicatePrimitives flag.
+ */
+ public void setFixupDuplicatePrimitives(boolean fixupDuplicatePrimitives)
+ {
+ }
+
+ /**
+ * Convert a string in JSON format into Java objects.
+ *
+ * @param jsonString The JSON format string.
+ * @return An object (or tree of objects) representing the data in the JSON
+ * format string.
+ * @throws UnmarshallException If unmarshalling fails
+ */
+ public Object fromJSON(String jsonString) throws UnmarshallException
+ {
+ return null;
+ }
+
+ /**
+ * Should serializers defined in this object include the fully qualified class
+ * name of objects being serialized? This can be helpful when unmarshalling,
+ * though if not needed can be left out in favor of increased performance and
+ * smaller size of marshalled String. Default is true.
+ *
+ * @return whether Java Class hints are included in the serialised JSON
+ * objects
+ */
+ public boolean getMarshallClassHints()
+ {
+ return false;
+ }
+
+ /**
+ * Returns true if attributes will null values should still be included in the
+ * serialized JSON object. Defaults to true. Set to false for performance
+ * gains and small JSON serialized size. Useful because null and undefined for
+ * JSON object attributes is virtually the same thing.
+ *
+ * @return boolean value as to whether null attributes will be in the
+ * serialized JSON objects
+ */
+ public boolean getMarshallNullAttributes()
+ {
+ return false;
+ }
+
+ /**
+ * Marshall java into an equivalent json representation (JSONObject or
+ * JSONArray.) This involves finding the correct Serializer for the class
+ * of the given java object and then invoking it to marshall the java object
+ * into json. The Serializer will invoke this method recursively while
+ * marshalling complex object graphs.
+ *
+ * @param state can be used by the underlying Serializer objects to hold state
+ * while marshalling.
+ *
+ * @param parent parent object of the object being converted. this can be null if
+ * it's the root object being converted.
+ * @param java java object to convert into json.
+ *
+ * @param ref reference within the parent's point of view of the object being serialized.
+ * this will be a String for JSONObjects and an Integer for JSONArrays.
+ *
+ * @return the JSONObject or JSONArray (or primitive object) containing the json
+ * for the marshalled java object or the special token Object,
+ * JSONSerializer.CIRC_REF_OR_DUP to indicate to the caller that the
+ * given Object has already been serialized and so therefore the result
+ * should be ignored.
+ *
+ * @throws MarshallException if there is a problem marshalling java to json.
+ */
+ public Object marshall(SerializerState state, Object parent, Object java, Object ref)
+ throws MarshallException
+ {
+ return null;
+ }
+
+ /**
+ * Register all of the provided standard serializers.
+ *
+ * @throws Exception If a serialiser has already been registered for a class.
+ *
+ * TODO: Should this be thrown: This can only happen if there is an internal
+ * problem with the code
+ */
+ public void registerDefaultSerializers() throws Exception
+ {
+ }
+
+ /**
+ * Register a new type specific serializer. The order of registration is
+ * important. More specific serializers should be added after less specific
+ * serializers. This is because when the JSONSerializer is trying to find a
+ * serializer, if it can't find the serializer by a direct match, it will
+ * search for a serializer in the reverse order that they were registered.
+ *
+ * @param s A class implementing the Serializer interface (usually derived
+ * from AbstractSerializer).
+ */
+ public void registerSerializer(Serializer s)
+ {
+ }
+
+ /**
+ * Should serializers defined in this object include the fully qualified class
+ * name of objects being serialized? This can be helpful when unmarshalling,
+ * though if not needed can be left out in favor of increased performance and
+ * smaller size of marshalled String. Default is true.
+ *
+ * @param marshallClassHints flag to enable/disable inclusion of Java class
+ * hints in the serialized JSON objects
+ */
+ public void setMarshallClassHints(boolean marshallClassHints)
+ {
+ }
+
+ /**
+ * Returns true if attributes will null values should still be included in the
+ * serialized JSON object. Defaults to true. Set to false for performance
+ * gains and small JSON serialized size. Useful because null and undefined for
+ * JSON object attributes is virtually the same thing.
+ *
+ * @param marshallNullAttributes flag to enable/disable marshalling of null
+ * attributes in the serialized JSON objects
+ */
+ public void setMarshallNullAttributes(boolean marshallNullAttributes)
+ {
+ }
+
+ /**
+ * Convert a Java objects (or tree of Java objects) into a string in JSON
+ * format. Note that this method will remove any circular references / duplicates
+ * and not handle the potential fixups that could be generated. (unless duplicates/circular
+ * references are turned off.
+ *
+ * todo: have some way to transmit the fixups back to the caller of this method.
+ *
+ * @param obj the object to be converted to JSON.
+ * @return the JSON format string representing the data in the the Java
+ * object.
+ * @throws MarshallException If marshalling fails.
+ */
+ public String toJSON(Object obj) throws MarshallException
+ {
+ return null;
+ }
+
+ /**
+ *
+ * Determine if a given JSON object matches a given class type, and to what
+ * degree it matches. An ObjectMatch instance is returned which contains a
+ * number indicating the number of fields that did not match. Therefore when a given
+ * parameter could potentially match in more that one way, this is a metric
+ * to compare these ObjectMatches to determine which one matches more closely.
+ *
+ * This is only used when there are overloaded method names that are being called
+ * from JSON-RPC to determine which call signature the method call matches most
+ * closely and therefore which method is the intended target method to call.
+ *
+ * @param state used by the underlying Serializer objects to hold state
+ * while unmarshalling for detecting circular references and duplicates.
+ *
+ * @param clazz optional java class to unmarshall to- if set to null then it
+ * will be looked for via the javaClass hinting mechanism.
+ *
+ * @param json JSONObject or JSONArray or primitive Object wrapper that contains the json to unmarshall.
+ *
+ * @return an ObjectMatch indicating the degree to which the object matched the class,
+ * @throws UnmarshallException if getClassFromHint() fails
+ */
+ public ObjectMatch tryUnmarshall(SerializerState state, Class clazz,
+ Object json) throws UnmarshallException
+ {
+ return null;
+ }
+
+ /**
+ * Unmarshall json into an equivalent java object. This involves finding
+ * the correct Serializer to use and then delegating to that Serializer to
+ * unmarshall for us. This method will be invoked recursively as Serializers
+ * unmarshall complex object graphs.
+ *
+ * @param state used by the underlying Serializer objects to hold state
+ * while unmarshalling for detecting circular references and duplicates.
+ *
+ * @param clazz optional java class to unmarshall to- if set to null then it
+ * will be looked for via the javaClass hinting mechanism.
+ *
+ * @param json JSONObject or JSONArray or primitive Object wrapper that contains the json to unmarshall.
+ *
+ * @return the java object representing the json that was unmarshalled.
+ *
+ * @throws UnmarshallException if there is a problem unmarshalling json to
+ * java.
+ */
+ public Object unmarshall(SerializerState state, Class clazz, Object json)
+ throws UnmarshallException
+ {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/MarshallException.java b/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/MarshallException.java
new file mode 100644
index 00000000000..73da08738d1
--- /dev/null
+++ b/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/MarshallException.java
@@ -0,0 +1,5 @@
+package org.jabsorb.serializer;
+
+public class MarshallException extends Exception
+{
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/ObjectMatch.java b/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/ObjectMatch.java
new file mode 100644
index 00000000000..d430b92ca0d
--- /dev/null
+++ b/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/ObjectMatch.java
@@ -0,0 +1,88 @@
+/*
+ * jabsorb - a Java to JavaScript Advanced Object Request Broker
+ * http://www.jabsorb.org
+ *
+ * Copyright 2007-2009 The jabsorb team
+ *
+ * based on original code from
+ * JSON-RPC-Java - a JSON-RPC to Java Bridge with dynamic invocation
+ *
+ * Copyright Metaparadigm Pte. Ltd. 2004.
+ * Michael Clark
+ *
+ * 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.
+ *
+ */
+
+package org.jabsorb.serializer;
+
+/**
+ *
+ * This class is returned from the Serializer tryUnmarshall method to indicate
+ * number of mismatched fields. This is used to handle ambiguities with
+ * JavaScript's typeless objects combined with and Java's operator overloading.
+ *
+ * TODO: wouldn't a better name for this class be ObjectMismatch as it's would
+ * be more descriptive. The name ObjectMatch is a little confusing because it
+ * implies the opposite of what the class actually stores (ObjectMismatch)
+ * either that, or I'm not understanding something correctly... [WB: I agree!]
+ */
+public class ObjectMatch
+{
+ /**
+ * Create a new ObjectMatch object with the given number of mismatches.
+ *
+ * @param mismatch the number of mismatched fields that occured on a
+ * tryUnmarshall call.
+ */
+ public ObjectMatch(int mismatch)
+ {
+ }
+
+ /**
+ * Get the number of mismatched fields that occured on a tryUnmarshall call.
+ *
+ * @return the number of mismatched fields that occured on a tryUnmarshall
+ * call.
+ */
+ public int getMismatch()
+ {
+ return -1;
+ }
+
+ /**
+ * Set the mismatch on this ObjectMatch.
+ * The ObjectMatch cannot be immutable anymore (at least in the current design--
+ * because the same mismatch object must be maintained through recursive processing
+ * to properly handle circular references detection)
+ *
+ * @param mismatch the mismatch value to set for this ObjectMatch.
+ */
+ public void setMismatch(int mismatch)
+ {
+ }
+
+ /**
+ * Compare another ObjectMatch with this ObjectMatch and return the one that
+ * has the most mismatches.
+ *
+ * @param m ObjectMatch to compare this ObjectMatch to.
+ *
+ * @return this ObjectMatch if it has more mismatches, else the passed in
+ * ObjectMatch.
+ */
+ public ObjectMatch max(ObjectMatch m)
+ {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/Serializer.java b/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/Serializer.java
new file mode 100644
index 00000000000..dd22f0c7cb6
--- /dev/null
+++ b/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/Serializer.java
@@ -0,0 +1,116 @@
+/*
+ * jabsorb - a Java to JavaScript Advanced Object Request Broker
+ * http://www.jabsorb.org
+ *
+ * Copyright 2007-2009 The jabsorb team
+ *
+ * based on original code from
+ * JSON-RPC-Java - a JSON-RPC to Java Bridge with dynamic invocation
+ *
+ * Copyright Metaparadigm Pte. Ltd. 2004.
+ * Michael Clark
+ *
+ * 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.
+ *
+ */
+
+package org.jabsorb.serializer;
+
+import java.io.Serializable;
+
+import org.jabsorb.JSONSerializer;
+
+/**
+ * Interface to be implemented by custom serializer objects that convert to and
+ * from Java objects and JSON objects.
+ */
+public interface Serializer extends Serializable
+{
+
+ /**
+ * Determine if the given java,json class pair can be handled by this
+ * serializer. Both for serialzing from java => json and deserializing from
+ * json => java.
+ *
+ * @param clazz java Class type.
+ * @param jsonClazz json Class wrapper type.
+ * @return true if this serializer can serialize/deserialize the given pair.
+ */
+ public boolean canSerialize(Class clazz, Class jsonClazz);
+
+ /**
+ * Get the json java classes that this Serializer is able to serialize from
+ * json into java and deserialize into json from java. These will
+ * typically be primitive class type wrappers or JSONObject, JSONArray.
+ *
+ * @return json side java classes that can be serialized/deserialized by this
+ * serializer.
+ */
+ public Class[] getJSONClasses();
+
+ /**
+ * Get the java classes that this Serializer is able to serialize from java
+ * into json and deserialize into java from json.
+ *
+ * @return java side classes that can be serialized/deserialized by this
+ * serializer.
+ */
+ public Class[] getSerializableClasses();
+
+ /**
+ * Marshall a java object into an equivalent json object.
+ *
+ * @param state can be used to hold state while unmarshalling through
+ * recursive levels.
+ * @param p parent of java object being marshalled into json (can be null if the object is the root object being marshalled.
+ * @param o java object to marhsall into json.
+ * @return that JSONObject or JSONArray that contains the json representation
+ * of the java object that was marshalled.
+ * @throws MarshallException if there is a problem marshalling java to json.
+ */
+ public Object marshall(SerializerState state, Object p, Object o)
+ throws MarshallException;
+
+ /**
+ * Set the owning JSONSerializer of this Serializer instance.
+ *
+ * @param ser the owning JSONSerializer of this Serializer instance.
+ */
+ public void setOwner(JSONSerializer ser);
+
+ /**
+ * Attempts to unmarshal a javascript object
+ *
+ * @param state The state of the serialiser
+ * @param clazz The class to unmarhall to
+ * @param json The object to unmarshal
+ * @return An ObjectMatch denoting whether the object matches the class (?)
+ * @throws UnmarshallException
+ */
+ public ObjectMatch tryUnmarshall(SerializerState state, Class clazz,
+ Object json) throws UnmarshallException;
+
+ /**
+ * Unmarshall json into an equivalent java object.
+ *
+ * @param state can be used to hold state while unmarshalling through
+ * recursive levels.
+ * @param clazz optional java class to unmarshall to.
+ * @param json JSONObject or JSONArray that contains the json to unmarshall.
+ * @return the java object representing the json that was unmarshalled.
+ * @throws UnmarshallException if there is a problem unmarshalling json to
+ * java.
+ */
+ public Object unmarshall(SerializerState state, Class clazz, Object json)
+ throws UnmarshallException;
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/SerializerState.java b/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/SerializerState.java
new file mode 100644
index 00000000000..67df4688934
--- /dev/null
+++ b/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/SerializerState.java
@@ -0,0 +1,98 @@
+/*
+ * jabsorb - a Java to JavaScript Advanced Object Request Broker
+ * http://www.jabsorb.org
+ *
+ * Copyright 2007-2009 The jabsorb team
+ *
+ * based on original code from
+ * JSON-RPC-Java - a JSON-RPC to Java Bridge with dynamic invocation
+ *
+ * Copyright Metaparadigm Pte. Ltd. 2004.
+ * Michael Clark
+ *
+ * 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.
+ *
+ */
+
+package org.jabsorb.serializer;
+
+import java.util.List;
+
+/**
+ * Used by Serializers to hold state during marshalling and
+ * unmarshalling. It keeps track of all Objects encountered
+ * during processing for the purpose of detecting circular
+ * references and/or duplicates.
+ */
+public class SerializerState
+{
+ /**
+ * Add a fixup entry. Assumes that the SerializerState is in the correct scope for the
+ * fix up location.
+ *
+ * @param originalLocation original json path location where the object was first encountered.
+ * @param ref additional reference (String|Integer) to add on to the scope's current location.
+ * @throws MarshallException if a scope error occurs (this won't normally occur.
+ */
+ public void addFixUp(List originalLocation, Object ref) throws MarshallException
+ {
+ }
+
+ /**
+ * Get the List of all FixUp objects created during processing.
+ * @return List of FixUps to circular references and duplicates found during processing.
+ */
+ public List getFixUps()
+ {
+ return null;
+ }
+
+ /**
+ * Pop off one level from the scope stack of the current location during processing.
+ * If we are already at the lowest level of scope, then this has no action.
+ * @throws MarshallException If called when currentLocation is empty
+ */
+ public void pop() throws MarshallException
+ {
+ }
+
+ /**
+ * Record the given object as a ProcessedObject and push into onto the scope stack. This is only
+ * used for marshalling. The store method should be used for unmarshalling.
+ *
+ * @param parent parent of object to process. Can be null if it's the root object being processed.
+ * it should be an object that was already processed via a previous call to processObject.
+ *
+ * @param obj object being processed
+ * @param ref reference to object within parent-- should be a String if parent is an object, and Integer
+ * if parent is an array. Can be null if this is the root object that is being pushed/processed.
+ */
+ public void push(Object parent, Object obj, Object ref)
+ {
+ }
+
+ /**
+ * Associate the incoming source object being serialized to it's serialized representation.
+ * Currently only used within tryUnmarshall and unmarshall. This MUST be called before a given unmarshall
+ * or tryUnmarshall recurses into child objects to unmarshall them.
+ * The purpose is to stop the recursion that can take place when circular references/duplicates are in the
+ * input json being unmarshalled.
+ *
+ * @param source source object being unmarshalled.
+ * @param target target serialized representation of the object that the source object is being unmarshalled to.
+ * @throws UnmarshallException if the source object is null, or is not already stored within a ProcessedObject.
+ */
+ public void setSerialized(Object source, Object target) throws UnmarshallException
+ {
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/UnmarshallException.java b/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/UnmarshallException.java
new file mode 100644
index 00000000000..cfff648e115
--- /dev/null
+++ b/java/ql/test/stubs/jabsorb-1.3.2/org/jabsorb/serializer/UnmarshallException.java
@@ -0,0 +1,5 @@
+package org.jabsorb.serializer;
+
+public class UnmarshallException extends Exception
+{
+}
\ No newline at end of file