diff --git a/src/main/java/tools/jackson/databind/ext/OptionalHandlerFactory.java b/src/main/java/tools/jackson/databind/ext/OptionalHandlerFactory.java index b125ae3425..fb956703e6 100644 --- a/src/main/java/tools/jackson/databind/ext/OptionalHandlerFactory.java +++ b/src/main/java/tools/jackson/databind/ext/OptionalHandlerFactory.java @@ -1,30 +1,22 @@ package tools.jackson.databind.ext; -import java.util.HashMap; -import java.util.Map; - import javax.xml.datatype.Duration; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; import tools.jackson.databind.*; -import tools.jackson.databind.ser.jdk.JavaUtilDateSerializer; +import tools.jackson.databind.ext.sql.JavaSqlTypeHandlerFactory; import tools.jackson.databind.ser.std.ToStringSerializer; -import tools.jackson.databind.util.ClassUtil; /** * Helper class used for isolating details of handling optional+external types * (javax.xml classes) from standard factories that offer them. - *

- * Note that with 3.0 need for separate class has been reduced somewhat - * and this class may be eliminated. */ public class OptionalHandlerFactory { - /* To make 2 main "optional" handler groups (javax.xml.stream) - * more dynamic, we better only figure out handlers completely dynamically, if and - * when they are needed. To do this we need to assume package prefixes. - */ + // To make 2 main "optional" handler groups (javax.xml.stream) + // more dynamic, we better only figure out handlers completely dynamically, if and + // when they are needed. To do this we need to assume package prefixes. private final static String PACKAGE_PREFIX_JAVAX_XML = "javax.xml."; // // Since 2.7, we will assume DOM classes are always found, both due to JDK 1.6 minimum @@ -35,41 +27,7 @@ public class OptionalHandlerFactory public final static OptionalHandlerFactory instance = new OptionalHandlerFactory(); - // classes from java.sql module, this module may or may not be present at runtime - // (is included on Java 8, but not part of JDK core for Java 9 and beyond) - private final Map _sqlDeserializers; - private final Map _sqlSerializers; - - private final static String CLS_NAME_JAVA_SQL_TIMESTAMP = "java.sql.Timestamp"; - private final static String CLS_NAME_JAVA_SQL_DATE = "java.sql.Date"; - private final static String CLS_NAME_JAVA_SQL_TIME = "java.sql.Time"; - private final static String CLS_NAME_JAVA_SQL_BLOB = "java.sql.Blob"; - private final static String CLS_NAME_JAVA_SQL_SERIALBLOB = "javax.sql.rowset.serial.SerialBlob"; - protected OptionalHandlerFactory() { - _sqlDeserializers = new HashMap<>(); - _sqlDeserializers.put(CLS_NAME_JAVA_SQL_DATE, - "tools.jackson.databind.ext.sql.JavaSqlDateDeserializer"); - _sqlDeserializers.put(CLS_NAME_JAVA_SQL_TIMESTAMP, - "tools.jackson.databind.ext.sql.JavaSqlTimestampDeserializer"); - // 09-Nov-2020, tatu: No deserializer for `java.sql.Blob` yet; would require additional - // dependency and not yet requested by anyone. Add if requested - - _sqlSerializers = new HashMap<>(); - // 09-Jan-2015, tatu: As per [databind#1073], let's try to guard against possibility - // of some environments missing `java.sql.` types - - // note: timestamps are very similar to java.util.Date, thus serialized as such - _sqlSerializers.put(CLS_NAME_JAVA_SQL_TIMESTAMP, JavaUtilDateSerializer.instance); - _sqlSerializers.put(CLS_NAME_JAVA_SQL_DATE, "tools.jackson.databind.ext.sql.JavaSqlDateSerializer"); - _sqlSerializers.put(CLS_NAME_JAVA_SQL_TIME, "tools.jackson.databind.ext.sql.JavaSqlTimeSerializer"); - - // 09-Nov-2020, tatu: Not really optimal way to deal with these, problem being that - // Blob is interface and actual instance we get is usually different. So may - // need to improve if we reported bugs. But for now, do this - - _sqlSerializers.put(CLS_NAME_JAVA_SQL_BLOB, "tools.jackson.databind.ext.sql.JavaSqlBlobSerializer"); - _sqlSerializers.put(CLS_NAME_JAVA_SQL_SERIALBLOB, "tools.jackson.databind.ext.sql.JavaSqlBlobSerializer"); } /* @@ -86,15 +44,6 @@ public ValueSerializer findSerializer(SerializationConfig config, JavaType ty } String className = rawType.getName(); - Object sqlHandler = _sqlSerializers.get(className); - - if (sqlHandler != null) { - if (sqlHandler instanceof ValueSerializer) { - return (ValueSerializer) sqlHandler; - } - // must be class name otherwise - return (ValueSerializer) instantiate((String) sqlHandler, type); - } if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML) || hasSuperClassStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) { if (Duration.class.isAssignableFrom(rawType) || QName.class.isAssignableFrom(rawType)) { return ToStringSerializer.instance; @@ -103,7 +52,7 @@ public ValueSerializer findSerializer(SerializationConfig config, JavaType ty return XMLGregorianCalendarSerializer.instance; } } - return null; + return JavaSqlTypeHandlerFactory.instance.findSerializer(config, type); } public ValueDeserializer findDeserializer(DeserializationConfig config, JavaType type) @@ -116,15 +65,11 @@ public ValueDeserializer findDeserializer(DeserializationConfig config, JavaT return new DOMDeserializer.DocumentDeserializer(); } String className = rawType.getName(); - final String deserName = _sqlDeserializers.get(className); - if (deserName != null) { - return (ValueDeserializer) instantiate(deserName, type); - } if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML) || hasSuperClassStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) { return CoreXMLDeserializers.findBeanDeserializer(config, type); } - return null; + return JavaSqlTypeHandlerFactory.instance.findDeserializer(config, type); } public boolean hasDeserializerFor(Class valueType) { @@ -140,8 +85,7 @@ public boolean hasDeserializerFor(Class valueType) { || hasSuperClassStartingWith(valueType, PACKAGE_PREFIX_JAVAX_XML)) { return CoreXMLDeserializers.hasDeserializerFor(valueType); } - // 06-Nov-2020, tatu: One of "java.sql" types? - return _sqlDeserializers.containsKey(className); + return JavaSqlTypeHandlerFactory.instance.hasDeserializerFor(valueType); } private boolean _IsXOfY(Class valueType, Class expType) { @@ -154,28 +98,6 @@ private boolean _IsXOfY(Class valueType, Class expType) { /********************************************************************** */ - private Object instantiate(String className, JavaType valueType) - { - try { - return instantiate(Class.forName(className), valueType); - } catch (Exception e) { - throw new IllegalStateException("Failed to find class `" -+className+"` for handling values of type "+ClassUtil.getTypeDescription(valueType) -+", problem: ("+e.getClass().getName()+") "+e.getMessage()); - } - } - - private Object instantiate(Class handlerClass, JavaType valueType) - { - try { - return ClassUtil.createInstance(handlerClass, false); - } catch (Exception e) { - throw new IllegalStateException("Failed to create instance of `" -+handlerClass.getName()+"` for handling values of type "+ClassUtil.getTypeDescription(valueType) -+", problem: ("+e.getClass().getName()+") "+e.getMessage()); - } - } - /** * Since 2.7 we only need to check for class extension, as all implemented * types are classes, not interfaces. This has performance implications for diff --git a/src/main/java/tools/jackson/databind/ext/beans/JavaBeansAnnotations.java b/src/main/java/tools/jackson/databind/ext/beans/JavaBeansAnnotations.java index a990230451..5f55468b01 100644 --- a/src/main/java/tools/jackson/databind/ext/beans/JavaBeansAnnotations.java +++ b/src/main/java/tools/jackson/databind/ext/beans/JavaBeansAnnotations.java @@ -8,8 +8,8 @@ /** * Since 2 JDK7-added annotations were left out of JDK 9+ core modules, - * moved into "java.beans", support for them will be left as dynamic - * for Jackson 3.x, and handled via this class + * moved into "java.beans" (module {@code java.desktop}), support for them + * will be left as dynamic for Jackson 3.x, and handled via this class */ public abstract class JavaBeansAnnotations { diff --git a/src/main/java/tools/jackson/databind/ext/sql/JavaSqlTypeHandlerFactory.java b/src/main/java/tools/jackson/databind/ext/sql/JavaSqlTypeHandlerFactory.java new file mode 100644 index 0000000000..0796ba6a19 --- /dev/null +++ b/src/main/java/tools/jackson/databind/ext/sql/JavaSqlTypeHandlerFactory.java @@ -0,0 +1,123 @@ +package tools.jackson.databind.ext.sql; + +import java.util.HashMap; +import java.util.Map; + +import tools.jackson.databind.*; +import tools.jackson.databind.ser.jdk.JavaUtilDateSerializer; +import tools.jackson.databind.util.ClassUtil; + +/** + * Helper class used for isolating details of handling optional+external types + * (java.sql classes) from standard factories that offer them. + */ +public class JavaSqlTypeHandlerFactory +{ + public final static JavaSqlTypeHandlerFactory instance = new JavaSqlTypeHandlerFactory(); + + // classes from java.sql module, this module may or may not be present at runtime + // (is included on Java 8, but not part of JDK core for Java 9 and beyond) + private final Map _sqlDeserializers; + private final Map _sqlSerializers; + + private final static String CLS_NAME_JAVA_SQL_TIMESTAMP = "java.sql.Timestamp"; + private final static String CLS_NAME_JAVA_SQL_DATE = "java.sql.Date"; + private final static String CLS_NAME_JAVA_SQL_TIME = "java.sql.Time"; + private final static String CLS_NAME_JAVA_SQL_BLOB = "java.sql.Blob"; + private final static String CLS_NAME_JAVA_SQL_SERIALBLOB = "javax.sql.rowset.serial.SerialBlob"; + + protected JavaSqlTypeHandlerFactory() { + _sqlDeserializers = new HashMap<>(); + _sqlDeserializers.put(CLS_NAME_JAVA_SQL_DATE, + "tools.jackson.databind.ext.sql.JavaSqlDateDeserializer"); + _sqlDeserializers.put(CLS_NAME_JAVA_SQL_TIMESTAMP, + "tools.jackson.databind.ext.sql.JavaSqlTimestampDeserializer"); + // 09-Nov-2020, tatu: No deserializer for `java.sql.Blob` yet; would require additional + // dependency and not yet requested by anyone. Add if requested + + _sqlSerializers = new HashMap<>(); + // 09-Jan-2015, tatu: As per [databind#1073], let's try to guard against possibility + // of some environments missing `java.sql.` types + + // note: timestamps are very similar to java.util.Date, thus serialized as such + _sqlSerializers.put(CLS_NAME_JAVA_SQL_TIMESTAMP, JavaUtilDateSerializer.instance); + _sqlSerializers.put(CLS_NAME_JAVA_SQL_DATE, "tools.jackson.databind.ext.sql.JavaSqlDateSerializer"); + _sqlSerializers.put(CLS_NAME_JAVA_SQL_TIME, "tools.jackson.databind.ext.sql.JavaSqlTimeSerializer"); + + // 09-Nov-2020, tatu: Not really optimal way to deal with these, problem being that + // Blob is interface and actual instance we get is usually different. So may + // need to improve if we reported bugs. But for now, do this + + _sqlSerializers.put(CLS_NAME_JAVA_SQL_BLOB, "tools.jackson.databind.ext.sql.JavaSqlBlobSerializer"); + _sqlSerializers.put(CLS_NAME_JAVA_SQL_SERIALBLOB, "tools.jackson.databind.ext.sql.JavaSqlBlobSerializer"); + } + + /* + /********************************************************************** + /* Public API + /********************************************************************** + */ + + public ValueSerializer findSerializer(SerializationConfig config, JavaType type) + { + final Class rawType = type.getRawClass(); + + String className = rawType.getName(); + Object sqlHandler = _sqlSerializers.get(className); + + if (sqlHandler != null) { + if (sqlHandler instanceof ValueSerializer) { + return (ValueSerializer) sqlHandler; + } + // must be class name otherwise + return (ValueSerializer) instantiate((String) sqlHandler, type); + } + return null; + } + + public ValueDeserializer findDeserializer(DeserializationConfig config, JavaType type) + { + final Class rawType = type.getRawClass(); + String className = rawType.getName(); + final String deserName = _sqlDeserializers.get(className); + if (deserName != null) { + return (ValueDeserializer) instantiate(deserName, type); + } + return null; + } + + public boolean hasDeserializerFor(Class valueType) { + String className = valueType.getName(); + + // 06-Nov-2020, tatu: One of "java.sql" types? + return _sqlDeserializers.containsKey(className); + } + + /* + /********************************************************************** + /* Internal helper methods + /********************************************************************** + */ + + private Object instantiate(String className, JavaType valueType) + { + try { + return instantiate(Class.forName(className), valueType); + } catch (Exception e) { + throw new IllegalStateException("Failed to find class `" ++className+"` for handling values of type "+ClassUtil.getTypeDescription(valueType) ++", problem: ("+e.getClass().getName()+") "+e.getMessage()); + } + } + + private Object instantiate(Class handlerClass, JavaType valueType) + { + try { + return ClassUtil.createInstance(handlerClass, false); + } catch (Exception e) { + throw new IllegalStateException("Failed to create instance of `" ++handlerClass.getName()+"` for handling values of type "+ClassUtil.getTypeDescription(valueType) ++", problem: ("+e.getClass().getName()+") "+e.getMessage()); + } + } +}