Skip to content

Commit

Permalink
Fix #1685
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Jul 19, 2017
1 parent 574d2bc commit b688919
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 45 deletions.
Expand Up @@ -3840,7 +3840,7 @@ public void acceptJsonFormatVisitor(Class<?> type, JsonFormatVisitorWrapper visi
{
acceptJsonFormatVisitor(_typeFactory.constructType(type), visitor);
}

/**
* Method for visiting type hierarchy for given type, using specified visitor.
* Visitation uses <code>Serializer</code> hierarchy and related properties
Expand All @@ -3861,7 +3861,7 @@ public void acceptJsonFormatVisitor(JavaType type, JsonFormatVisitorWrapper visi
}
_serializerProvider(getSerializationConfig()).acceptJsonFormatVisitor(type, visitor);
}

/*
/**********************************************************
/* Internal methods for serialization, overridable
Expand Down
Expand Up @@ -1422,7 +1422,7 @@ protected final DateFormat _dateFormat()
if (_dateFormat != null) {
return _dateFormat;
}
/* At this point, all timezone configuration should have occured, with respect
/* At this point, all timezone configuration should have occurred, with respect
* to default dateformat configuration. But we still better clone
* an instance as formatters are stateful, not thread-safe.
*/
Expand Down
Expand Up @@ -37,19 +37,12 @@ protected long _timestamp(Calendar value) {
}

@Override
public void serialize(Calendar value, JsonGenerator jgen, SerializerProvider provider) throws IOException
public void serialize(Calendar value, JsonGenerator g, SerializerProvider provider) throws IOException
{
if (_asTimestamp(provider)) {
jgen.writeNumber(_timestamp(value));
} else if (_customFormat != null) {
// 21-Feb-2011, tatu: not optimal, but better than alternatives:
synchronized (_customFormat) {
// _customformat cannot parse Calendar, so Date should be passed
jgen.writeString(_customFormat.format(value.getTime()));
}
} else {
provider.defaultSerializeDateValue(value.getTime(), jgen);
g.writeNumber(_timestamp(value));
return;
}
_serializeAsString(value.getTime(), g, provider);
}

}
Expand Up @@ -42,17 +42,12 @@ protected long _timestamp(Date value) {
}

@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException
public void serialize(Date value, JsonGenerator g, SerializerProvider provider) throws IOException
{
if (_asTimestamp(provider)) {
gen.writeNumber(_timestamp(value));
} else if (_customFormat != null) {
// 21-Feb-2011, tatu: not optimal, but better than alternatives:
synchronized (_customFormat) {
gen.writeString(_customFormat.format(value));
}
} else {
provider.defaultSerializeDateValue(value, gen);
g.writeNumber(_timestamp(value));
return;
}
_serializeAsString(value, g, provider);
}
}
Expand Up @@ -4,8 +4,10 @@
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicReference;

import com.fasterxml.jackson.annotation.JsonFormat;

Expand Down Expand Up @@ -35,12 +37,23 @@ public abstract class DateTimeSerializerBase<T>
*/
protected final DateFormat _customFormat;

/**
* If {@link #_customFormat} is used, we will try to reuse instances in simplest
* possible form; thread-safe, but without overhead of <code>ThreadLocal</code>
* (not from code, but wrt retaining of possibly large number of format instances
* over all threads, properties with custom formats).
*
* @since 2.9
*/
protected final AtomicReference<DateFormat> _reusedCustomFormat;

protected DateTimeSerializerBase(Class<T> type,
Boolean useTimestamp, DateFormat customFormat)
{
super(type);
_useTimestamp = useTimestamp;
_customFormat = customFormat;
_reusedCustomFormat = (customFormat == null) ? null : new AtomicReference<DateFormat>();
}

public abstract DateTimeSerializerBase<T> withFormat(Boolean timestamp, DateFormat customFormat);
Expand Down Expand Up @@ -189,4 +202,29 @@ protected void _acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaTy
visitStringFormat(visitor, typeHint, JsonValueFormat.DATE_TIME);
}
}

/**
* @since 2.9
*/
protected void _serializeAsString(Date value, JsonGenerator g, SerializerProvider provider) throws IOException
{
if (_customFormat == null) {
provider.defaultSerializeDateValue(value, g);
return;
}

// 19-Jul-2017, tatu: Here we will try a simple but (hopefully) effective mechanism for
// reusing formatter instance. This is our second attempt, after initially trying simple
// synchronization (which turned out to be bottleneck for some users in production...).
// While `ThreadLocal` could alternatively be used, it is likely that it would lead to
// higher memory footprint, but without much upside -- if we can not reuse, we'll just
// clone(), which has some overhead but not drastic one.

DateFormat f = _reusedCustomFormat.getAndSet(null);
if (f == null) {
f = (DateFormat) _customFormat.clone();
}
g.writeString(f.format(value));
_reusedCustomFormat.compareAndSet(null, f);
}
}
Expand Up @@ -40,34 +40,21 @@ protected long _timestamp(java.sql.Date value) {
}

@Override
public void serialize(java.sql.Date value, JsonGenerator gen, SerializerProvider provider)
public void serialize(java.sql.Date value, JsonGenerator g, SerializerProvider provider)
throws IOException
{
if (_asTimestamp(provider)) {
gen.writeNumber(_timestamp(value));
} else if (_customFormat != null) {
// 11-Oct-2016, tatu: As per [databind#219], same as with `java.util.Date`
synchronized (_customFormat) {
gen.writeString(_customFormat.format(value));
}
} else {
g.writeNumber(_timestamp(value));
return;
}
// Alas, can't just call `_serializeAsString()`....
if (_customFormat == null) {
// 11-Oct-2016, tatu: For backwards-compatibility purposes, we shall just use
// the awful standard JDK serialization via `sqlDate.toString()`... this
// is problematic in multiple ways (including using arbitrary timezone...)
gen.writeString(value.toString());
g.writeString(value.toString());
return;
}
}

@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
//todo: (ryan) add a format for the date in the schema?
return createSchemaNode("string", true);
}

@Override
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException
{
_acceptJsonFormatVisitor(visitor, typeHint, _useTimestamp);
_serializeAsString(value, g, provider);
}
}

0 comments on commit b688919

Please sign in to comment.