Integración en JAX-RS de la nueva libreria de tiempo de Java 8
Con la introducción de las nuevas clases de java.time
en Java 8 podemos encontrarnos con la conveniencia de querer utilizarlas como parámetros en algunos métodos REST de nuestra API JAX-RS.
A continuación muestro un ejemplo real de una aplicación en la que utilizo dos tipos de java.time
como parámetros de puntos REST. Concretamente las clases LocalDate
y MonthDay
.
Para añadir el soporte tan sólo tenemos que registrar un proveedor JAX-RS que implemente la interfaz ParamConverterProvider
.
@Provider
public class ParamConverters implements ParamConverterProvider {
@SuppressWarnings("unchecked")
@Override
public <T> ParamConverter<T> getConverter(Class<T> rawType,
Type genericType,
Annotation[] annotations)
{
if (rawType == LocalDate.class)
return (ParamConverter<T>) new ParamConverter<LocalDate>() {
@Override
public LocalDate fromString(String value) {
return TimeUtils.localDateFromJSON(value);
}
@Override
public String toString(LocalDate value) {
return null;
}
};
else if (rawType == MonthDay.class)
return (ParamConverter<T>) new ParamConverter<MonthDay>() {
@Override
public MonthDay fromString(String value) {
return TimeUtils.parseMonthDay(value);
}
@Override
public String toString(MonthDay value) {
return null;
}
};
return null;
}
}
La clase TimeUtils
es una clase propia que utilizo para encapsular algunos métodos extra relaciones con java.time
. Las partes interesantes son estas:
public class TimeUtils {
private static final Logger log = LoggerFactory.getLogger(TimeUtils.class);
public static LocalDate localDateFromJSON(String data) {
if (data.endsWith("Z")) {
// les dates serialitzades via JSON ho fan amb aquest format
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
return Instant.parse(data)
.atZone(ZoneId.systemDefault())
.toLocalDate();
} else
return LocalDate.parse(data);
}
public static MonthDay parseMonthDay(String s) {
try {
int[] ddmm = Arrays.stream(s.split("/"))
.mapToInt(Integer::parseInt)
.toArray();
return MonthDay.of(ddmm[1], ddmm[0]);
} catch (Exception e) {
log.warn("No he pogut interpretar {} com MonthDay.", s);
return null;
}
}
}
De la anterior clase la parte más notable es un pequeño hackeo para soportar correctamente las fechas en Javascript tal como se indica en el comentario. Dichas fechas se codifican siempre con el mismo formato relativo a UTC independientemente de la zona horaria local. Así que si queremos convertirlo a un LocalDate
debemos suponer alguna zona horaria. En el caso de esta aplicación no habia soporte para zonas horarias y todo funcionaba en la misma, clientes y servidor, así que de ahí el uso de la zona de sistema.