online joda examples dates current create java jersey jax-rs jodatime

java - examples - Usando Joda DateTime como un parámetro de Jersey?



jodatime java 8 (4)

Me gustaría utilizar el DateTime de Joda para los parámetros de consulta en Jersey, pero esto no es compatible con los modelos de DateTime de Jersey. Supongo que la implementación de un InjectableProvider es la forma adecuada de agregar compatibilidad con DateTime .

¿Alguien puede indicarme una buena implementación de un InjectableProvider para DateTime ? ¿O hay un enfoque alternativo que valga la pena recomendar? (Soy consciente de que puedo convertir desde Date o String en mi código, pero esto parece una solución menor).

Gracias.

Solución:

@Context la respuesta de Gili a continuación para usar el mecanismo de inyección de @Context en JAX-RS en lugar de Guice.

Actualización: Es posible que esto no funcione correctamente si UriInfo no se incluye en los parámetros de su método de servicio.

import com.sun.jersey.core.spi.component.ComponentContext; import com.sun.jersey.spi.inject.Injectable; import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider; import java.util.List; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; import javax.ws.rs.ext.Provider; import org.joda.time.DateTime; /** * Enables DateTime to be used as a QueryParam. * <p/> * @author Gili Tzabari */ @Provider public class DateTimeInjector extends PerRequestTypeInjectableProvider<QueryParam, DateTime> { private final UriInfo uriInfo; /** * Creates a new DateTimeInjector. * <p/> * @param uriInfo an instance of {@link UriInfo} */ public DateTimeInjector( @Context UriInfo uriInfo) { super(DateTime.class); this.uriInfo = uriInfo; } @Override public Injectable<DateTime> getInjectable(final ComponentContext cc, final QueryParam a) { return new Injectable<DateTime>() { @Override public DateTime getValue() { final List<String> values = uriInfo.getQueryParameters().get(a.value()); if( values == null || values.isEmpty()) return null; if (values.size() > 1) { throw new WebApplicationException(Response.status(Status.BAD_REQUEST). entity(a.value() + " may only contain a single value").build()); } return new DateTime(values.get(0)); } }; } }



Aquí hay una implementación que depende de Guice. Puede usar un inyector diferente con modificaciones menores:

import com.google.inject.Inject; import com.sun.jersey.core.spi.component.ComponentContext; import com.sun.jersey.spi.inject.Injectable; import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider; import java.util.List; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; import javax.ws.rs.ext.Provider; import org.joda.time.DateTime; /** * Enables DateTime to be used as a QueryParam. * <p/> * @author Gili Tzabari */ @Provider public class DateTimeInjector extends PerRequestTypeInjectableProvider<QueryParam, DateTime> { private final com.google.inject.Provider<UriInfo> uriInfo; /** * Creates a new DateTimeInjector. * <p/> * @param uriInfo an instance of {@link UriInfo} */ @Inject public DateTimeInjector(com.google.inject.Provider<UriInfo> uriInfo) { super(DateTime.class); this.uriInfo = uriInfo; } @Override public Injectable<DateTime> getInjectable(final ComponentContext cc, final QueryParam a) { return new Injectable<DateTime>() { @Override public DateTime getValue() { final List<String> values = uriInfo.get().getQueryParameters().get(a.value()); if (values.size() > 1) { throw new WebApplicationException(Response.status(Status.BAD_REQUEST). entity(a.value() + " may only contain a single value").build()); } if (values.isEmpty()) return null; return new DateTime(values.get(0)); } }; } }

No hay enlaces de Guice. @Provider es una anotación JAX-RS. Guice solo necesita poder inyectar UriInfo y Jersey-Guice proporciona ese enlace.


@ Gili, lo siento, no tengo la reputación requerida para comentar directamente tu publicación, pero podrías por favor:

  • agregar las declaraciones de importación utilizadas para su implementación?
  • agrega un ejemplo de cómo enlazar todo con Guice?

Muchas gracias por adelantado.

METRO.

PROBLEMAS :

Me interesaría hacer lo mismo que HolySamosa, y también uso Guice, pero enfrento los siguientes problemas.

Si agrego:

bind(DateTimeInjector.class);

en mi GuiceServletContextListener , obtengo:

java.lang.RuntimeException: The scope of the component class com.foo.mapping.DateTimeInjector must be a singleton

y si agrego @Singleton en la clase DateTimeInjector , obtengo:

GRAVE: The following errors and warnings have been detected with resource and/or provider classes: SEVERE: Missing dependency for method public java.util.List com.foo.ThingService.getThingByIdAndDate(java.lang.String,org.joda.time.DateTime) at parameter at index 1 SEVERE: Method, public java.util.List com.foo.ThingService.getThingByIdAndDate(java.lang.String,org.joda.time.DateTime), annotated with GET of resource, class com.foo.ThingService, is not recognized as valid resource method.

CONSEJOS / SOLUCIONES :

  • ¡Presta atención a la anotación que usas (a diferencia de mí)! Por ejemplo, en realidad estaba usando @PathParam lugar de @QueryParam .
  • En su servicio, no necesita tener UriInfo uriInfo en la firma del método. Solo los parámetros funcionales deberían ser suficientes y debería funcionar si UriInfo está presente o no.
  • Se necesita configurar Guice con el siguiente para poder recoger el inyector.

Ejemplo:

// Configure Jersey with Guice: Map<String, String> settings = new HashMap<String, String>(); settings.put(PackagesResourceConfig.PROPERTY_PACKAGES, "com.foo.mapping"); serve("/*").with(GuiceContainer.class, settings);

SOLUCIÓN COMPLETA :

import java.util.List; import javax.ws.rs.PathParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; import javax.ws.rs.ext.Provider; import org.joda.time.DateTime; import com.google.inject.Inject; import com.foo.utils.DateTimeAdapter; import com.sun.jersey.core.spi.component.ComponentContext; import com.sun.jersey.spi.inject.Injectable; import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider; /** * Enables DateTime to be used as a PathParam. */ @Provider public class DateTimeInjector extends PerRequestTypeInjectableProvider<PathParam, DateTime> { private final com.google.inject.Provider<UriInfo> uriInfo; /** * Creates a new DateTimeInjector. * * @param uriInfo * an instance of {@link UriInfo} */ @Inject public DateTimeInjector(com.google.inject.Provider<UriInfo> uriInfo) { super(DateTime.class); this.uriInfo = uriInfo; } @Override public Injectable<DateTime> getInjectable(final ComponentContext context, final PathParam annotation) { return new Injectable<DateTime>() { @Override public DateTime getValue() { final List<String> values = uriInfo.get().getPathParameters().get(annotation.value()); if (values == null) { throwInternalServerError(annotation); } if (values.size() > 1) { throwBadRequestTooManyValues(annotation); } if (values.isEmpty()) { throwBadRequestMissingValue(annotation); } return parseDate(annotation, values); } private void throwInternalServerError(final PathParam annotation) { String errorMessage = String.format("Failed to extract parameter [%s] using [%s]. This is likely to be an implementation error.", annotation.value(), annotation.annotationType().getName()); throw new WebApplicationException(Response.status(Status.INTERNAL_SERVER_ERROR).entity(errorMessage).build()); } private void throwBadRequestTooManyValues(final PathParam annotation) { String errorMessage = String.format("Parameter [%s] must only contain one single value.", annotation.value()); throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(errorMessage).build()); } private void throwBadRequestMissingValue(final PathParam annotation) { String errorMessage = String.format("Parameter [%s] must be provided.", annotation.value()); throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(errorMessage).build()); } private DateTime parseDate(final PathParam annotation, final List<String> values) { try { return DateTimeAdapter.parse(values.get(0)); } catch (Exception e) { String errorMessage = String.format("Parameter [%s] is formatted incorrectly: %s", annotation.value(), e.getMessage()); throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(errorMessage).build()); } } }; } }


Otra opción para tratar el envío de objetos Joda DateTime entre el cliente-servidor es ordenarlos / descalificarlos explícitamente usando un adaptador y una anotación de acuerdo. El principio es reunirlo como objeto Largo mientras desmantela la instancia de un nuevo objeto DateTime utilizando el objeto Long para la llamada del constructor. El objeto Long se obtiene a través del método getMillis. Para tener este trabajo, especifique el adaptador para usar en las clases que tienen un objeto DateTime:

@XmlElement(name="capture_date") @XmlJavaTypeAdapter(XmlDateTimeAdapter.class) public DateTime getCaptureDate() { return this.capture_date; } public void setCaptureDate(DateTime capture_date) { this.capture_date = capture_date; }

Luego escriba el adaptador y la clase XML para encapsular el objeto Long:

import javax.xml.bind.annotation.adapters.XmlAdapter; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; /** * Convert between joda datetime and XML-serialisable millis represented as long */ public class XmlDateTimeAdapter extends XmlAdapter<XmlDateTime, DateTime> { @Override public XmlDateTime marshal(DateTime v) throws Exception { if(v != null) return new XmlDateTime(v.getMillis()); else return new XmlDateTime(0); } @Override public DateTime unmarshal(XmlDateTime v) throws Exception { return new DateTime(v.millis, DateTimeZone.UTC); } } import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; /** * XML-serialisable wrapper for joda datetime values. */ @XmlRootElement(name="joda_datetime") public class XmlDateTime { @XmlElement(name="millis") public long millis; public XmlDateTime() {}; public XmlDateTime(long millis) { this.millis = millis; } }

Si todo va según lo planeado, los objetos DateTime deben organizarse / desmantelarse usando el adaptador; compruebe esto estableciendo puntos de interrupción en el adaptador.