JAX-RSをやってみる (13) - Refの依存性注入に関して -

2014-06-27T00:00:00+00:00 Java JAX-RS

前回も記述したけど、JspTemplateProcessorでは

final class JspTemplateProcessor extends AbstractTemplateProcessor<String> {

    @Inject
    private Provider<Ref<HttpServletRequest>> requestProviderRef;
    @Inject
    private Provider<Ref<HttpServletResponse>> responseProviderRef;

みたいになっとる訳で、テストで使用する際にあたってそういうところの依存性の注入の解決がうまくいかないのがあったので普通に@Contextで注入する的な事をしてましたけど、なんとか出来たっぽいので

ThymeleafTemplateProcessor.java

package sample;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;

import org.glassfish.jersey.internal.util.collection.Ref;
import org.glassfish.jersey.server.mvc.Viewable;
import org.glassfish.jersey.server.mvc.spi.AbstractTemplateProcessor;
import org.jvnet.hk2.annotations.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;

public class ThymeleafTemplateProcessor extends AbstractTemplateProcessor<String> {

    @Inject
    Provider<Ref<HttpServletRequest>> requestRef;

    @Inject
    Provider<Ref<HttpServletResponse>> responseRef;

    ServletContext servletContext;

    @Inject
    public ThymeleafTemplateProcessor(Configuration config, @Optional ServletContext servletContext) {
        super(config, servletContext, "thymeleaf", "html");
        this.servletContext = servletContext;
    }

    @SuppressWarnings("unchecked")
    @Override
    public void writeTo(String templateReference, Viewable viewable,
        MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
        OutputStream out) throws IOException {

        try(Writer writer = new OutputStreamWriter(out)) {
            Object o = viewable.getModel();
            Map<String, Object> stash;

            if (o instanceof Map) {
                stash = (Map<String, Object>)o;
            } else {
                stash = new HashMap<String, Object>();

                if (o instanceof Exception) {
                    stash.put("error", o);
                } else {
                    stash.put("it", o);
                }
            }

            WebContext context = new WebContext(
                requestRef.get().get(),
                responseRef.get().get(),
                servletContext
            );
            context.setVariables(stash);

            TemplateEngine engine = new TemplateEngine();
            engine.setTemplateResolver(new ClassLoaderTemplateResolver());
            engine.process(templateReference, context, writer);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected String resolve(String templatePath, Reader reader) throws Exception {
        return templatePath;
    }
}

的な感じで、テスト時に依存性を注入する必要があるのが

  • Provider>
  • Provider<Ref<HttpServletResponse>>
  • @Optional ServletContext

以上の3つの依存性の注入の解決が必要になるはず

HomeTest.java

package sample.controller;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Application;

import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.internal.inject.ReferencingFactory;
import org.glassfish.jersey.internal.util.collection.Ref;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;

import sample.SampleApplication;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;

public class HomeTest extends JerseyTest {

    MockHttpServletRequest  request  = new MockHttpServletRequest();
    MockHttpServletResponse response = new MockHttpServletResponse();

    @Override
    protected Application configure() {
        return new SampleApplication()
            .register(new AbstractBinder() {
                @Override
                protected void configure() {
                    bindFactory(
                        ReferencingFactory.<HttpServletRequest>referenceFactory(request)
                    ).to(new TypeLiteral<Ref<HttpServletRequest>>() {});

                    bindFactory(
                        ReferencingFactory.<HttpServletResponse>referenceFactory(response)
                    ).to(new TypeLiteral<Ref<HttpServletResponse>>() {});

                    bindFactory(new Factory<ServletContext>() {

                        @Override
                        public ServletContext provide() {
                            return new MockServletContext();
                        }

                        @Override
                        public void dispose(ServletContext instance) {
                        }
                    }).to(ServletContext.class);
                }
            });
    }

    @Test
    public void test1() {
        String response = target("/sample/test1").request().get(String.class);
        assertThat(response, containsString("hoge"));
    }
}

っていう感じでReferencingFactoryを使ってやれば良い模様

JAX-RSをやってみる (12) - MVC -