2012-11-20 17 views
11

Chciałbym użyć parametru DateTime dla Jody w parametrach zapytania w Jersey, ale nie jest to obsługiwane przez aplikację Jersey po instalacji. Zakładam, że implementacja InjectableProvider jest właściwym sposobem dodania obsługi DateTime.Używanie Joda DateTime jako parametru Jersey?

Czy ktoś może wskazać mi na dobrą implementację InjectableProvider dla DateTime? Czy istnieje alternatywne podejście godne polecenia? (Jestem świadomy, że mogę przekonwertować z Date lub String w moim kodzie, ale wydaje się to mniejszym rozwiązaniem).

Dzięki.

Rozwiązanie:

I zmodyfikowana odpowiedź Gili jest poniżej używać mechanizmu @Context wtrysku w JAX-RS zamiast Guice.

Aktualizacja: To może nie działać poprawnie, jeśli UriInfo nie zostanie wprowadzone w parametrach metody obsługi.

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)); 
      } 
     }; 
    } 
} 

Odpowiedz

5

Oto implementacja, która zależy od Guice. Można użyć innego wtryskiwacza z niewielkimi modyfikacjami:

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)); 
      } 
     }; 
    } 
} 

Brak powiązań Guice. @Provider to adnotacja JAX-RS. Guice po prostu musi być w stanie wstrzyknąć UriInfo i Jersey-Guice zapewnia to wiązanie.

+0

Dzięki, Gili. Nie używam Guice, ale zastanawiam się, czy UriInfo można wstrzyknąć za pomocą wstrzyknięcia JAX-RS @Context? – HolySamosa

+0

Yep, @Context służy do wstrzykiwania UriInfo. Również nieznacznie zmodyfikowałem twój kod, aby obsłużyć przypadek zerowych parametrów zapytania DateTime. Moja adaptacja została opublikowana jako edycja mojego pytania. Dzięki!! – HolySamosa

1

@ Gili, przepraszam, nie mam wymaganej reputacji bezpośrednio komentować Twój post, ale czy mógłbyś:

  • dodać instrukcje importu używanych do realizacji?
  • dodać przykład, jak powiązać wszystko z Guice?

Dziękuję bardzo z góry.

M.


PROBLEMY:

byłbym zainteresowany robi to samo co HolySamosa i używam Guice jako dobrze, ale twarz poniższe kwestie.

Jeśli dodam:

bind(DateTimeInjector.class); 

w moim GuiceServletContextListener, otrzymuję:

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

a jeśli dodać @Singleton od klasy DateTimeInjector, otrzymuję:

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. 

PORADY/Rozwiązania:

  • zwrócić uwagę na to, co adnotacja użyć (w przeciwieństwie do mnie)! Na przykład faktycznie używałem @PathParam zamiast @QueryParam.
  • W swojej usłudze nie trzeba mieć UriInfo uriInfo w podpisie metody. Wystarczy, że parametry funkcjonalne będą wystarczające i powinno zadziałać, niezależnie od tego, czy występuje UriInfo.
  • Guice należy skonfigurować, aby móc podnieść wtryskiwacz.

Przykład:

// 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); 

pełne rozwiązanie:

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()); 
       } 
      } 

     }; 
    } 
} 
+0

Zaktualizowałem swoją odpowiedź. – Gili

+0

@Marc: Zobacz wersję niegebice w mojej edycji na pytanie. – HolySamosa

+0

@HolySamosa: użycie '@ Context' bez Guice wydaje się nie działać, jeśli nie podano" UriInfo uriInfo "w parametrach metody usługi. Czy są dostępne tylko parametry DateTime lub niektóre inne parametry Jersey? na przykład 'public void saveResource (@Context UriInfo uriInfo, @QueryParam (" date ") Data DateTime) {...}' Czy używasz innej "sztuczki"? –

2

Inną opcją do czynienia z wysłaniem Joda DateTime obiektów między klient-serwer jest marszałek/usuń je jawnie, używając adaptera i odpowiedniej adnotacji. Zasada polega na tym, aby ustawić go jako obiekt długi, a podczas rozstawiania utworzyć nowy obiekt DateTime za pomocą obiektu Long dla wywołania konstruktora. Obiekt Long jest uzyskiwany za pomocą metody getMillis. Aby mieć tę pracę, określ adapter do wykorzystania w zajęciach, które mają obiekt 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; } 

następnie napisać adapter i klasę XML do hermetyzacji Long obiektu:

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; } 

} 

Jeśli wszystko idzie zgodnie z planem, obiekty DateTime powinny zostać rozesłane/rozesłane za pomocą adaptera; sprawdź to, ustawiając punkty przerwania w adapterze.

Powiązane problemy