Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDI API for injection of configuration values #42

Closed
wants to merge 12 commits into from
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ tck/.checkstyle
tck/bin/
.project
build/
.classpath
.classpath
5 changes: 5 additions & 0 deletions api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2016-2017 Payara Services Ltd. and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package io.microprofile.config.inject;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.enterprise.util.Nonbinding;
import javax.inject.Qualifier;

/**
* Binds the injection point with a configured value.
* Should annotate injection points of type ConfigValue<T>, where T can be String and all types which have appropriate converters.
* <p>
* Example:
* <pre>
* <code>
*
* {@literal @Inject}
* {@literal @ConfigProperty("my.long.property")}
* {@literal ConfigValue<Long>} longConfigValue;
* // injects a ConfigValue for the value of my.long.property property to resolve the property dynamically
* </code>
* </pre>
* @author Ondrej Mihalyi
*/
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface ConfigProperty {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far, I've seen people using 2 different naming conventions for this kind of annotation and a new injection point type (discussed here):

  • @Inject @PropertyConfig("mykey") PropertyValue<String> mykeyValue
  • @Inject @ConfigProperty("mykey") ConfigValue<String> mykeyValue

Please comment here to let me know which is better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Inject @ConfigProperty("mykey") ConfigValue mykeyValue
makes more sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For developers, I don't think the default name would start with class name. What is the normal prefix? I think for the config source under meta-inf\microprofile-config.properties, the key will not have any prefix. For the config source files outside the app, it depends. If the source is for a particular app, it might have the prefix with the app name etc. Therefore, I think it is better not to prebake any prefix.

Copy link
Contributor

@smillidge smillidge Feb 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree classname.package name is a good default as @OndrejM says this is more amenable to refactoring while still providing a level of name scoping. Obviously the developer can still specify the name directly in the annotation. I'm just advocating this as a default if no name is specified.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend to use classname.fieldName for the default. It is not common to use package name as part of property name. By the way, the classname should be the classname with the first letter in lower case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Emily-Jiang: I recommend to use classname.fieldName for the default.

It is already stated in the javadoc as you recommended: "class_name is the simple name of the class being injected to". Is that OK or am I missing something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the javadoc, {@code classname} is the simple name of the class being injected to with the first letter being decapitalised. e.g. If the class name
Public class File{
@Inject @ConfigProperty ConfigValue name; => this should map to file.name property.
}

/**
* The kay of the config property used to look up the configuration value.
* If it is not specified, it will be derived automatically as {@code <class_name>.<injetion_point_name>},
* where {@code injection_point_name} is either a field name or a property name in case of field/property injection,
* {@code class_name} is the simple name of the class being injected to.
* If one of the {@code class_name} or {@code injection_point_name} cannot be determined, the value has to be provided.
*
* @return Name (key) of the config property to inject
*/
@Nonbinding
String value() default "";
}
49 changes: 49 additions & 0 deletions api/src/main/java/org/eclipse/microprofile/config/ConfigValue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2016-2017 Payara Services Ltd. and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.eclipse.microprofile.config;

/**
* Accessor to a configured value. Suitable when a configured value should be
* accessed dynamically or additional metadata is necessary on top of the
* configured value.
*
* @author Ondrej Mihalyi
*
* @param <T_VALUE> Type of the configuration value
*/
public interface ConfigValue<T_VALUE> {

/**
* Returns the converted resolved value.
*
* @return the resolved value
*/
T_VALUE getValue();

/**
* Whether the value is defined in one of the config sources or not.
* @return True if a value for the key is defined, false otherwise
*/
boolean isValueAvailable();

/**
* Returns the key used to retrieve the configuration value.
*
* @return the original key
*/
String getKey();
}
30 changes: 28 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<maven.compiler.target>1.8</maven.compiler.target>

<checkstyle.version>2.17</checkstyle.version>
<checkstyle.methodNameFormat>^_?[a-z][a-zA-Z0-9]*$</checkstyle.methodNameFormat>
</properties>

<licenses>
Expand All @@ -32,13 +33,38 @@
<module>tck</module>
<module>spec</module>
</modules>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>1.1.12.Final</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${checkstyle.version}</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${checkstyle.version}</version>
<executions>
<execution>
<id>verify-style</id>
Expand Down Expand Up @@ -71,7 +97,7 @@
</module>
<module name="LocalVariableName" />
<module name="MethodName">
<property name="format" value="^_?[a-z][a-zA-Z0-9]*$" />
<property name="format" value="${checkstyle.methodNameFormat}" />
</module>
<module name="PackageName" />
<module name="LocalFinalVariableName" />
Expand Down
54 changes: 52 additions & 2 deletions tck/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
<artifactId>microprofile-config-tck</artifactId>
<version>1.0-SNAPSHOT</version>


<properties>
<checkstyle.methodNameFormat>^_?[a-z][a-zA-Z0-9_]*$</checkstyle.methodNameFormat>
</properties>

<dependencies>
<dependency>
<groupId>org.eclipse.microprofile.config.api</groupId>
Expand All @@ -27,6 +30,53 @@
<version>6.9.9</version>
<scope>compile</scope>
</dependency>
</dependencies>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-weld-embedded</artifactId>
<version>2.0.0.Beta4</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-core</artifactId>
<version>2.4.1.Final</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>org.jboss.shrinkwrap</groupId>
<artifactId>shrinkwrap-api</artifactId>
<scope>compile</scope>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2016-2017 Payara Services Ltd. and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.eclipse.microprofile.config.tck;

import io.microprofile.config.inject.ConfigProperty;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import org.eclipse.microprofile.config.ConfigValue;
import static org.eclipse.microprofile.config.tck.testsupport.TestSetup.ensure_property_defined;
import static org.eclipse.microprofile.config.tck.testsupport.TestSetup.ensure_property_undefined;
import static org.eclipse.microprofile.config.tck.testsupport.TestSetup.get_bean_of_type;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
* Test cases for CDI-based API that test retrieving values from the configuration.
* The tests depend only on CDI 1.2.
* @author Ondrej Mihalyi
*/
@RunWith(Arquillian.class)
public class CDIPlainInjectionTest {

@Deployment
public static Archive deployment() {
return ShrinkWrap.create(JavaArchive.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}

@Test
public void can_inject_dynamic_values_via_ConfigValue() {
clear_all_property_values();

DynamicValuesBean bean = get_bean_of_type(DynamicValuesBean.class);
ConfigValue<Integer> intPropertyValue = bean.getIntPropertyValue();

assertThat(intPropertyValue.isValueAvailable(), is(false));

ensure_all_property_values_are_defined();

assertThat(intPropertyValue.isValueAvailable(), is(true));
assertThat(intPropertyValue.getValue(), is(equalTo(5)));
}

private void ensure_all_property_values_are_defined() {
ensure_property_defined("my.string.property", "text");
ensure_property_defined("my.boolean.property", "true");
ensure_property_defined("my.int.property", "5");
ensure_property_defined("my.long.property", "10");
ensure_property_defined("my.float.property", "10.5");
ensure_property_defined("my.double.property", "11.5");
ensure_property_defined("my.date.property", "2017-01-30");
ensure_property_defined("my.localdate.property", "2016-01-30");
ensure_property_defined("my.datetime.property", "2015-01-30T10:00");
}

private void clear_all_property_values() {
ensure_property_undefined("my.string.property");
ensure_property_undefined("my.boolean.property");
ensure_property_undefined("my.int.property");
ensure_property_undefined("my.long.property");
ensure_property_undefined("my.float.property");
ensure_property_undefined("my.double.property");
ensure_property_undefined("my.date.property");
ensure_property_undefined("my.localdate.property");
ensure_property_undefined("my.datetime.property");
}

@Dependent
public static class DynamicValuesBean {

@Inject
@ConfigProperty("my.int.property")
private ConfigValue<Integer> intPropertyValue;

public ConfigValue<Integer> getIntPropertyValue() {
return intPropertyValue;
}

}

}
Loading