Using a custom hk2 InjectionResolver to inject application configuration

前端 未结 1 1218
傲寒
傲寒 2021-01-13 03:04

Kind of a follow up to my previous question. I\'m trying to inject application configuration data using JSR-330 standard annotations and the HK2 framework bundled with jerse

相关标签:
1条回答
  • 2021-01-13 03:58

    "My problem is that application.getProperties() is empty. Any idea what's wrong?

    No. This actually works perfectly fine for me.

    public class ConfigurationInjectionResolver implements InjectionResolver<Named> {  
        @Context
        Application application;
    
        @Override
        public Object resolve(Injectee injectee, ServiceHandle<?> root) {
            Named annotation = injectee.getParent().getAnnotation(Named.class);
            Map<String, Object> props = application.getProperties();
            String name = annotation.value();
            System.out.println(props.get(name));
            return props.get(name);
        }
    
        @Override
        public boolean isConstructorParameterIndicator() { return false; }
        @Override
        public boolean isMethodParameterIndicator() { return false; }  
    }
    
    @ApplicationPath("/rest")
    public class JerseyApplication extends ResourceConfig {
    
        public JerseyApplication() {
            packages("jersey.startup.test");
            property("hello.config", "Hello World Property");
            register(new AbstractBinder() {
                @Override
                protected void configure() {
                    bind(ConfigurationInjectionResolver.class)
                            .to(new TypeLiteral<InjectionResolver<Named>>() {
                            }).in(Singleton.class);
                }
            });
        }
    }
    

    Resource

    @Path("/config")
    public class ConfigResource {
    
        @Named("hello.config")
        String hello;
    
        @GET
        public Response getHello() {
            return Response.ok(hello).build();
        }
    }
    

    C:\>curl http://localhost:8080/test/rest/config
    Hello World Property

    Personally though, in this situation, I would create my own annotation, as to not override any existing functionality of the @Named annotation.


    Another cool option

    HK2 has a configuration extension, where you can load a Properties object from say a .properties file and and have those properties automatically injected with the @Configured annotation. I couldn't find any documentation on this, but there is an example usage of it in the HK2 source code examples.

    Here's an example implementation

    Required dependencies. Check the Jersey version and see what HK2 version it depends on. In my case Jersey 2.13 uses HK2 2.3.0-b10, so that should be the ${hk2.version}

    <dependency>
        <groupId>org.glassfish.hk2</groupId>
        <artifactId>hk2-configuration-hub</artifactId>
        <version>${hk2.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.hk2</groupId>
        <artifactId>hk2-configuration-integration</artifactId>
        <version>${hk2.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.hk2</groupId>
        <artifactId>hk2-property-file</artifactId>
        <version>${hk2.version}</version>
    </dependency>
    

    App config

    @ApplicationPath("/rest")
    public class JerseyApplication extends ResourceConfig {
    
        @Inject
        public JerseyApplication(ServiceLocator locator) {
            packages("jersey.startup.test");
            ServiceLocatorUtilities.addClasses(locator, ConfigResource.class);
            try {
                loadConfigurationProperties(locator);
            } catch (IOException ex) {
                Logger.getLogger(JerseyApplication.class.getName())
                                       .log(Level.SEVERE, null, ex);
            }
        }
    
        private void loadConfigurationProperties(ServiceLocator locator) 
                                                     throws IOException {
            ConfigurationUtilities.enableConfigurationSystem(locator);
            PropertyFileUtilities.enablePropertyFileService(locator);
            PropertyFileService propertyFileService 
                    = locator.getService(PropertyFileService.class);
            Properties props = new Properties();
            URL url = getClass().getResource("/configuration.properties");
            props.load(url.openStream());
            PropertyFileHandle propertyFileHandle 
                    = propertyFileService.createPropertyHandleOfAnyType();
            propertyFileHandle.readProperties(props);
        }
    }
    

    configuration.properties

    AppConfiguration.App.hello=Hello Squirrel Property!
    

    Resource

    @Path("/config")
    @ConfiguredBy("AppConfiguration")
    public class ConfigResource {
    
        @Configured
        String hello;
    
        @GET
        public Response getHello() {
            return Response.ok(hello).build();
        }
    }
    

    C:\>curl http://localhost:8080/test/rest/config
    Hello Squirrel Property!

    Diclaimer: Since this feature isn't well documented, I am not sure if I have a good implementation here. It is just by trial and error. For instance this

    ServiceLocatorUtilities.addClasses(locator, ConfigResource.class);
    

    I feel shouldn't be necessary. It seems redundant, as I am already package scanning. So to explicitly add the ConfigResource to the locator context doesn't seem right to me.

    0 讨论(0)
提交回复
热议问题