users@jersey.java.net

[Jersey] StringReaderWorkers in InjectableProvider with jersey-guice

From: Adam Zegelin <adam_at_relational.io>
Date: Tue, 12 Feb 2013 12:57:49 +1100

Hi All,

I’m trying to write a custom context injectable provider that maps request form params to Form (a class of ours) object fields which are strongly-typed and would like to reuse jersey’s built-in StringReaderWorkers to do the string -> instance-of-type conversions.

The idea being that I can define Form subclasses and then use `_at_Conext SubclassOfForm form` injection on resource methods.

There doesn’t seem to be an obvious way to obtain an instance of StringReaderWorkers from within a Provider.

I was initially using Jersey injection to obtain an instance of ProviderServices in my InjectableProvider subclass constructor. I could then later create an instance of StringReaderFactory and call `stringReaderFactory.init(providerServices)` with the obtained ProviderServices instance. I found that trying to jersey-inject StringReaderWorkers directly in the ctor failed due to a race condition; InjectableProviders are created/inited in the WebApplicationImpl before StringReaderWorkers.

I’d like to use Guice injection (i’m using jersey-guice) on our Form subclasses and thus had to switch to Guice injection on my InjectableProvider subclass ctor to obtain an Injector instance. I found that I can access an instance of my WebApplication and obtain a ServerInjectableProviderContext, which I can then use to “inject” a StringReaderWorkers instance.

I’ve included a copy of my InjectableProvider below. I’d appreciate it if somebody could have a look and let me know if there is a better method of obtaining the StringReaderWorkers instance used by the FormInjectable (inner class).

Regards,
Adam


@Provider
public class FormProvider implements InjectableProvider<Context, Class> {
    private final Injector injector;

    @Inject
    public FormProvider(Injector injector) {
        this.injector = injector;
    }

    public class FormInjectable extends AbstractHttpContextInjectable<Form> {
        private final StringReaderWorkers stringReaderWorkers;
        private final Class<? extends Form> clazz;

        public FormInjectable(StringReaderWorkers stringReaderWorkers, final Class<? extends Form> clazz) {
            this.stringReaderWorkers = stringReaderWorkers;
            this.clazz = clazz;
        }

        @Override
        public Form getValue(HttpContext c) {
            final Form form = injector.getInstance(clazz);

            final com.sun.jersey.api.representation.Form params = c.getRequest().getFormParameters();

            for (final Map.Entry<String, Form.Field> fieldEntry: form.fields.entrySet()) {
                final Form.Field field = fieldEntry.getValue();
                final String rawParam = params.getFirst(fieldEntry.getKey());

                try {
                    final Object param = stringReaderWorkers.getStringReader(field.type.getRawType(), field.type.getType(), null).fromString(rawParam);

                    field.withValue(param);
                } catch (NullPointerException e) {
                    // ignore -- field will be null

                } catch (ExtractorContainerException e) {
                    field.withRawValue(rawParam).withValid(false).withMessage("Please provide a valid value.");
                }
            }

            form.validate();

            return form;
        }
    }

    @Override
    public Injectable<Form> getInjectable(final ComponentContext ic, final Context a, final Class clazz) {
        if (!Form.class.isAssignableFrom(clazz))
            return null;

        final WebApplication webApp = injector.getInstance(WebApplication.class);
        final ServerInjectableProviderContext sipc = webApp.getServerInjectableProviderFactory();

        final StringReaderWorkers srw = (StringReaderWorkers) sipc.getInjectable(new Parameter(ic.getAnnotations(), a, null, null, StringReaderWorkers.class, StringReaderWorkers.class), null).getValue();

        return new FormInjectable(srw, clazz);
    }

    @Override
    public ComponentScope getScope() {
        return ComponentScope.PerRequest;
    }
}