On Oct 28, 2008, at 10:50 PM, Jeff Schmidt wrote:
> Hello:
>
> This seems like such a basic thing to want, that I'm probably just
> missing something there. I have a method in a resource defined as:
>
> @GET
> public AnalysesList getInfoForAllAnalyses(
> @Context UriInfo uriInfo,
> @QueryParam("analysistype") AnalysisType analysisType) {
>
> ...
> }
>
> AnalysisType is a pretty simple Enum:
>
> @XmlEnum
> public enum AnalysisType {
>
> @XmlEnumValue("Core")
> CORE("Core"),
> @XmlEnumValue("Tox")
> TOX("Tox"),
> @XmlEnumValue("Metabolomics")
> METABOLOMICS("Metabolomics")
> ;
>
> private final String value;
>
> AnalysisType(String v) {
> value = v;
> }
>
> public String value() {
> return value;
> }
>
> public static AnalysisType fromValue(String v) {
> for (AnalysisType c: AnalysisType.values()) {
> if (c.value.equals(v)) {
> return c;
> }
> }
> throw new IllegalArgumentException(v.toString());
> }
>
> /*
> public static AnalysisType valueOf(String typeStr) {
> return AnalysisType.fromValue(typeStr);
> }*/
> }
>
> Note this Enum was originally derived from an XML schema used in a
> SOAP API (using JAXB xjc), and it created the fromValue() method,
> which I assume JAXB uses to bind a String representation to an
> actual Enum instance. I would also like to bind a JAX-RS query
> parameter to this Enum using Jersey. Making a request with the query
> parameter defined as analysistype=CORE works great. However, I want
> to refer to the type as "Core" (analysistype=Core) to match the
> CamelCase XML standard we're using. However, this results in 404
> (Not Found).
>
> I don't want to have to change the Enum itself and dependent code
> to switch from AnalysisType.CORE to AnalysisType.Core. From what I
> can tell, a given Java class can be bound by Jersey if it has a
> static valueOf(String) method. However, Enum will not let me define
> this method as the compiler says its already defined. I'm guessing
> that's how Jersey is able to bind as explained earlier; based on the
> actual name of the Enum elements.
Yes, i think that is what is going on.
> Also, I cannot make a constructor available to Jersey, given it's an
> Enum.
>
> Is there a good way to handle this in Jersey? Perhaps there is some
> kind of provider or resolver I have to implement on my own?
>
The simplest approach is to create a wrapper class supporting the
static valueOf or a constructor taking a String argument.
You can register an injectable for the @QueryParam and the AnalysisType:
@Provider
public static class QueryParamInjectableProvider implements
InjectableProvider<QueryParam, Parameter> {
private final @Context HttpContext hc;
public QueryParamInjectableProvider(@Context HttpContext hc) {
this.hc = hc;
}
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
public Injectable<AnalysisType>
getInjectable(ComponentContext ic,
QueryParam a, Parameter c) {
if (AnalysisType.class != c.getParameterClass())
return null;
final String name = c.getSourceName();
return new Injectable<AnalysisType>() {
public AnalysisType getValue() {
String value =
hc.getUriInfo().getQueryParameters().getFirst(name);
return AnalysisType.fromValue(name);
}
};
}
}
Note that the above does not support default values (in the absence of
the query parameter), but it is easy to add, and it should throw a 404-
based exception if the value is not recognized.
I am also wondering whether it should be possible to add support for
any enum with a particular method like fromValue.
@Provider
public static class QueryParamInjectableProvider implements
InjectableProvider<QueryParam, Parameter> {
private final @Context HttpContext hc;
public QueryParamInjectableProvider(@Context HttpContext hc) {
this.hc = hc;
}
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
public Injectable<? extends Enum>
getInjectable(ComponentContext ic,
QueryParam a, Parameter c) {
if
(AnalysisType.class.isAssignableFrom(c.getParameterClass())
return null;
....
}
}
Hope this helps,
Paul.