Drew13
Drew13

Reputation: 1371

Incorrect parameter type on DateTime

Intellij is telling me that updateTime is of incorrect parameter type. I'm unfamiliar with this error and @EnumDateFormat.

    @DELETE
    @Path("groups/{groupId}/samples/{testYear}")
    @Produces(MediaType.APPLICATION_JSON)
    @RolesAllowed({Profile.MP_USER_ROLE})
    public Response deleteGroupSamples (@PathParam("groupId") Long groupId, @PathParam("testYear") Long testYear, @QueryParam("updateTime") @EnumDateFormat( FormatEnum.WITH_HHMMSS ) DateTime updateTime, TreatmentGroupTest sample) throws ValidationException{
        manager.deleteGroupSample( this.getUser(), sample.getTestSampleId(), updateTime );
        ResponseBuilder builder=null;
        builder = Response.ok(  );
        return builder.build();
    }

The error also suggests:

Checks types of parameters @PathParam, @QueryParam, etc. The type of annotated parameter, field or property must either

  1. Be a primative type

  2. Have a constructor that accepts a single String argument

  3. Have a static method named valueOf or formString that accepts a single String argument (see, for example, Integer.valueOf(String))

  4. Have a registered implementation of ParamConverterProvider JAX-RS extension SPI that returns a ParamConverter instance capable of a "form string" conversion of that type

  5. Be List, Set or SortedSet, where T satisfies 2, 3 or 4 above. The resulting collection is read-only

Upvotes: 2

Views: 3445

Answers (1)

Juan Marcos Bellini
Juan Marcos Bellini

Reputation: 73

I guess this is an old question, but for LocalDate you must register an implementation of ParamConverterProvider that returns a ParamConverter instance that is capable of creating a LocalDate from a String.

If you check the javadoc for ParamConverter, you will see that there are two methods: E fromString(String value) and String toString(E value). Also, if you see the documentation for ParamConverterProvider you will notice that it has the following method: <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation annotations[]).

Basically, you must create an implementation of ParamConverter<LocalDate> that can create a LocalDate from a String, and then return an instance of that implementation from a registered ParamConverterProvider.

Here is an example:

package com.example;

import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Optional;

/**
 * The {@link ParamConverterProvider} for {@link java.time} package classes
 * (i.e {@link LocalDate}, {@link LocalTime} and {@link LocalDateTime}).
 */
@Provider
public class Java8TimeParamConverterProvider implements ParamConverterProvider {

    /**
     * Contains a {@link DateTimeFormatter} whose pattern is "yyyy-MM-dd".
     */
    public static final DateTimeFormatter CLASSIC_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");


    @Override
    public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
        if (rawType == LocalDate.class) {
            //noinspection unchecked
            return (ParamConverter<T>) new Java8LocalDateClassicFormatParamConverter();
        }
        if (rawType == LocalTime.class) {
            return null; // TODO: implement
        }
        if (rawType == LocalDateTime.class) {
            return null; // TODO: implement
        }
        return null;
    }

    /**
     * The {@link ParamConverter} for {@link LocalDate} using the {@link #CLASSIC_DATE_FORMATTER}.
     */
    private static class Java8LocalDateClassicFormatParamConverter implements ParamConverter<LocalDate> {

        @Override
        public LocalDate fromString(String value) {
            return Optional.ofNullable(value)
                    .map(CLASSIC_DATE_FORMATTER::parse)
                    .map(LocalDate::from)
                    .orElse(null);
        }

        @Override
        public String toString(LocalDate value) {
            return CLASSIC_DATE_FORMATTER.format(value);
        }
    }
}

You must register this provider. In your ResourceConfig you must include this:

public class AppConfig extends ResourceConfig {

    public AppConfig() {
        register(Java8TimeParamConverterProvider.class);
    }
}

Or, if you are doing package scanning:

public class AppConfig extends ResourceConfig {

    public AppConfig() {
        packages("com.example");
    }
}

(This is why the @Providerannotation is included in the Java8TimeParamConverterProviderclass definition). Note that this won't work if you are using spring boot, as there are issues at this moment with the final jar created by the spring boot plugin.

Another way to register your provider is by the web.xml. In this case you must include the following:

<servlet>
    <servlet-name>jersey-servlet</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>

    <!-- Include the following init-param if you want to do package scanning -->
    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>
            com.example
        </param-value>
    </init-param>
    <!-- Or use the following init-param if you want to do class register -->
    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>com.example.Java8TimeParamConverterProvider</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

Hope this helps you!

Upvotes: 6

Related Questions