Search code examples
javajerseyjax-rsjodatime

Using Joda DateTime as a Jersey parameter?


I'd like to use Joda's DateTime for query parameters in Jersey, but this isn't supported by Jersey out-of-the-box. I'm assuming that implementing an InjectableProvider is the proper way to add DateTime support.

Can someone point me to a good implementation of an InjectableProvider for DateTime? Or is there an alternative approach worth recommending? (I'm aware I can convert from Date or String in my code, but this seems like a lesser solution).

Thanks.

Solution:

I modified Gili's answer below to use the @Context injection mechanism in JAX-RS rather than Guice.

Update: This may not work properly if UriInfo isn't injected in your service method parameters.

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

Solution

  • Here is an implementation that depends on Guice. You can using a different injector with minor modifications:

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

    There are no Guice bindings. @Provider is a JAX-RS annotation. Guice just needs to be able to inject UriInfo and Jersey-Guice provides that binding.