Skip to content

Commit a2887f5

Browse files
committed
HDDS-1468. Inject configuration values to Java objects
Closes #772
1 parent f1673b0 commit a2887f5

File tree

11 files changed

+438
-30
lines changed

11 files changed

+438
-30
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
* <p>
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* <p>
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hdds.conf;
19+
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
import java.util.concurrent.TimeUnit;
25+
26+
/**
27+
* Mark field to be configurable from ozone-site.xml.
28+
*/
29+
@Retention(RetentionPolicy.RUNTIME)
30+
@Target(ElementType.METHOD)
31+
public @interface Config {
32+
33+
/**
34+
* Configuration fragment relative to the prefix defined with @ConfigGroup.
35+
*/
36+
String key();
37+
38+
/**
39+
* Type of configuration. Use AUTO to decide it based on the java type.
40+
*/
41+
ConfigType type() default ConfigType.AUTO;
42+
43+
/**
44+
* If type == TIME the unit should be defined with this attribute.
45+
*/
46+
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
47+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
* <p>
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* <p>
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hdds.conf;
19+
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
/**
26+
* Mark pojo which holds configuration variables.
27+
*/
28+
@Retention(RetentionPolicy.RUNTIME)
29+
@Target(ElementType.TYPE)
30+
public @interface ConfigGroup {
31+
String prefix();
32+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
* <p>
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* <p>
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hdds.conf;
19+
20+
/**
21+
* Possible type of injected configuration.
22+
* <p>
23+
* AUTO means that the exact type will be identified based on the java type of
24+
* the configuration field.
25+
*/
26+
public enum ConfigType {
27+
AUTO,
28+
STRING,
29+
BOOLEAN,
30+
INT,
31+
LONG,
32+
TIME,
33+
SIZE
34+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
* <p>
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* <p>
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hdds.conf;
19+
20+
/**
21+
* Exeception to throw in case of a configuration problem.
22+
*/
23+
public class ConfigurationException extends RuntimeException {
24+
public ConfigurationException() {
25+
}
26+
27+
public ConfigurationException(String message) {
28+
super(message);
29+
}
30+
31+
public ConfigurationException(String message, Throwable cause) {
32+
super(message, cause);
33+
}
34+
}

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/conf/OzoneConfiguration.java

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import javax.xml.bind.annotation.XmlAccessorType;
2929
import javax.xml.bind.annotation.XmlElement;
3030
import javax.xml.bind.annotation.XmlRootElement;
31+
import java.lang.reflect.InvocationTargetException;
32+
import java.lang.reflect.Method;
3133
import java.net.URL;
3234
import java.util.ArrayList;
3335
import java.util.Enumeration;
@@ -61,6 +63,108 @@ public List<Property> readPropertyFromXml(URL url) throws JAXBException {
6163
return config.getProperties();
6264
}
6365

66+
/**
67+
* Create a Configuration object and inject the required configuration values.
68+
*
69+
* @param configurationClass The class where the fields are annotated with
70+
* the configuration.
71+
* @return Initiated java object where the config fields are injected.
72+
*/
73+
public <T> T getObject(Class<T> configurationClass) {
74+
75+
T configuration;
76+
77+
try {
78+
configuration = configurationClass.newInstance();
79+
} catch (InstantiationException | IllegalAccessException e) {
80+
throw new ConfigurationException(
81+
"Configuration class can't be created: " + configurationClass, e);
82+
}
83+
ConfigGroup configGroup =
84+
configurationClass.getAnnotation(ConfigGroup.class);
85+
String prefix = configGroup.prefix();
86+
87+
for (Method setterMethod : configurationClass.getMethods()) {
88+
if (setterMethod.isAnnotationPresent(Config.class)) {
89+
90+
String methodLocation =
91+
configurationClass + "." + setterMethod.getName();
92+
93+
Config configAnnotation = setterMethod.getAnnotation(Config.class);
94+
95+
String key = prefix + "." + configAnnotation.key();
96+
97+
Class<?>[] parameterTypes = setterMethod.getParameterTypes();
98+
if (parameterTypes.length != 1) {
99+
throw new ConfigurationException(
100+
"@Config annotation should be used on simple setter: "
101+
+ methodLocation);
102+
}
103+
104+
ConfigType type = configAnnotation.type();
105+
106+
if (type == ConfigType.AUTO) {
107+
type = detectConfigType(parameterTypes[0], methodLocation);
108+
}
109+
110+
//Note: default value is handled by ozone-default.xml. Here we can
111+
//use any default.
112+
try {
113+
switch (type) {
114+
case STRING:
115+
setterMethod.invoke(configuration, get(key));
116+
break;
117+
case INT:
118+
setterMethod.invoke(configuration,
119+
getInt(key, 0));
120+
break;
121+
case BOOLEAN:
122+
setterMethod.invoke(configuration,
123+
getBoolean(key, false));
124+
break;
125+
case LONG:
126+
setterMethod.invoke(configuration,
127+
getLong(key, 0));
128+
break;
129+
case TIME:
130+
setterMethod.invoke(configuration,
131+
getTimeDuration(key, 0, configAnnotation.timeUnit()));
132+
break;
133+
default:
134+
throw new ConfigurationException(
135+
"Unsupported ConfigType " + type + " on " + methodLocation);
136+
}
137+
} catch (InvocationTargetException | IllegalAccessException e) {
138+
throw new ConfigurationException(
139+
"Can't inject configuration to " + methodLocation, e);
140+
}
141+
142+
}
143+
}
144+
return configuration;
145+
146+
}
147+
148+
private ConfigType detectConfigType(Class<?> parameterType,
149+
String methodLocation) {
150+
ConfigType type;
151+
if (parameterType == String.class) {
152+
type = ConfigType.STRING;
153+
} else if (parameterType == Integer.class || parameterType == int.class) {
154+
type = ConfigType.INT;
155+
} else if (parameterType == Long.class || parameterType == long.class) {
156+
type = ConfigType.LONG;
157+
} else if (parameterType == Boolean.class
158+
|| parameterType == boolean.class) {
159+
type = ConfigType.BOOLEAN;
160+
} else {
161+
throw new ConfigurationException(
162+
"Unsupported configuration type " + parameterType + " in "
163+
+ methodLocation);
164+
}
165+
return type;
166+
}
167+
64168
/**
65169
* Class to marshall/un-marshall configuration from xml files.
66170
*/
@@ -145,7 +249,7 @@ public String toString() {
145249
}
146250

147251
@Override
148-
public int hashCode(){
252+
public int hashCode() {
149253
return this.getName().hashCode();
150254
}
151255

@@ -169,6 +273,7 @@ public static void activate() {
169273
* does not override values of properties
170274
* if there is no tag present in the configs of
171275
* newly added resources.
276+
*
172277
* @param tag
173278
* @return Properties that belong to the tag
174279
*/
@@ -181,7 +286,7 @@ public Properties getAllPropertiesByTag(String tag) {
181286
Properties props = new Properties();
182287
Enumeration properties = propertiesByTag.propertyNames();
183288
while (properties.hasMoreElements()) {
184-
Object propertyName = properties.nextElement();
289+
Object propertyName = properties.nextElement();
185290
// get the current value of the property
186291
Object value = updatedProps.getProperty(propertyName.toString());
187292
if (value != null) {
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
* <p>
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* <p>
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hdds.conf;
19+
20+
import java.util.concurrent.TimeUnit;
21+
22+
/**
23+
* Example configuration to test the configuration injection.
24+
*/
25+
@ConfigGroup(prefix = "ozone.scm.client")
26+
public class SimpleConfiguration {
27+
28+
private String clientAddress;
29+
30+
private String bindHost;
31+
32+
private boolean enabled;
33+
34+
private int port = 1234;
35+
36+
private long waitTime = 1;
37+
38+
@Config(key = "address")
39+
public void setClientAddress(String clientAddress) {
40+
this.clientAddress = clientAddress;
41+
}
42+
43+
@Config(key = "bind.host")
44+
public void setBindHost(String bindHost) {
45+
this.bindHost = bindHost;
46+
}
47+
48+
@Config(key = "enabled")
49+
public void setEnabled(boolean enabled) {
50+
this.enabled = enabled;
51+
}
52+
53+
@Config(key = "port")
54+
public void setPort(int port) {
55+
this.port = port;
56+
}
57+
58+
@Config(key = "wait", type = ConfigType.TIME, timeUnit =
59+
TimeUnit.SECONDS)
60+
public void setWaitTime(long waitTime) {
61+
this.waitTime = waitTime;
62+
}
63+
64+
public String getClientAddress() {
65+
return clientAddress;
66+
}
67+
68+
public String getBindHost() {
69+
return bindHost;
70+
}
71+
72+
public boolean isEnabled() {
73+
return enabled;
74+
}
75+
76+
public int getPort() {
77+
return port;
78+
}
79+
80+
public long getWaitTime() {
81+
return waitTime;
82+
}
83+
}

0 commit comments

Comments
 (0)