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

HDDS-1468. Inject configuration values to Java objects #772

Closed
wants to merge 4 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
type-safe-config-api
  • Loading branch information
elek committed Apr 25, 2019

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 0dfb4c832093e4b7d32f2b436ab09f4b3bc226a9
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.apache.hadoop.hdds.conf;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Mark field to be configuratble from ozone-site.xml.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Config {
String key();

ConfigType type() default ConfigType.AUTO;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.apache.hadoop.hdds.conf;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Mark pojo which holds configuration variables.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ConfigGroup {
String prefix();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.apache.hadoop.hdds.conf;

/**
* Possible type of injected configuration.
* <p>
* AUTO means that the exact type will be identified based on the java type of
* the configuration field.
*/
public enum ConfigType {
AUTO,
STRING,
BOOLEAN,
INT,
LONG,
MILLISECONDS
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.apache.hadoop.hdds.conf;

/**
* Exeception to throw in case of a configuration problem.
*/
public class ConfigurationException extends RuntimeException {
public ConfigurationException() {
}

public ConfigurationException(String message) {
super(message);
}

public ConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -28,11 +28,14 @@
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
* Configuration for ozone.
@@ -61,6 +64,108 @@ public List<Property> readPropertyFromXml(URL url) throws JAXBException {
return config.getProperties();
}

/**
* Create a Configuration object and inject the required configuration values.
*
* @param configurationClass The class where the fields are annotated with
* the configuration.
* @return Initiated java object where the config fields are injected.
*/
public <T> T getObject(Class<T> configurationClass) {

T configuration;

try {
configuration = configurationClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new ConfigurationException(
"Configuration class can't be created: " + configurationClass, e);
}
ConfigGroup configGroup =
configurationClass.getAnnotation(ConfigGroup.class);
String prefix = configGroup.prefix();

for (Method setterMethod : configurationClass.getMethods()) {
if (setterMethod.isAnnotationPresent(Config.class)) {

String methodLocation =
configurationClass + "." + setterMethod.getName();

Config configAnnotation = setterMethod.getAnnotation(Config.class);

String key = prefix + "." + configAnnotation.key();

Class<?>[] parameterTypes = setterMethod.getParameterTypes();
if (parameterTypes.length != 1) {
throw new ConfigurationException(
"@Config annotation should be used on simple setter: "
+ methodLocation);
}

ConfigType type = configAnnotation.type();

if (type == ConfigType.AUTO) {
type = detectConfigType(parameterTypes[0], methodLocation);
}

//Note: default value is handled by ozone-default.xml. Here we can
//use any default.
try {
switch (type) {
case STRING:
setterMethod.invoke(configuration, get(key));
break;
case INT:
setterMethod.invoke(configuration,
getInt(key, 0));
break;
case BOOLEAN:
setterMethod.invoke(configuration,
getBoolean(key, false));
break;
case LONG:
setterMethod.invoke(configuration,
getLong(key, 0));
break;
case MILLISECONDS:
setterMethod.invoke(configuration,
getTimeDuration(key, 0, TimeUnit.MILLISECONDS));
break;
default:
throw new ConfigurationException(
"Unsupported ConfigType " + type + " on " + methodLocation);
}
} catch (InvocationTargetException | IllegalAccessException e) {
throw new ConfigurationException(
"Can't inject configuration to " + methodLocation, e);
}

}
}
return configuration;

}

private ConfigType detectConfigType(Class<?> parameterType,
String methodLocation) {
ConfigType type;
if (parameterType == String.class) {
type = ConfigType.STRING;
} else if (parameterType == Integer.class || parameterType == int.class) {
type = ConfigType.INT;
} else if (parameterType == Long.class || parameterType == long.class) {
type = ConfigType.LONG;
} else if (parameterType == Boolean.class
|| parameterType == boolean.class) {
type = ConfigType.BOOLEAN;
} else {
throw new ConfigurationException(
"Unsupported configuration type " + parameterType + " in "
+ methodLocation);
}
return type;
}

/**
* Class to marshall/un-marshall configuration from xml files.
*/
@@ -145,7 +250,7 @@ public String toString() {
}

@Override
public int hashCode(){
public int hashCode() {
return this.getName().hashCode();
}

@@ -169,6 +274,7 @@ public static void activate() {
* does not override values of properties
* if there is no tag present in the configs of
* newly added resources.
*
* @param tag
* @return Properties that belong to the tag
*/
@@ -181,7 +287,7 @@ public Properties getAllPropertiesByTag(String tag) {
Properties props = new Properties();
Enumeration properties = propertiesByTag.propertyNames();
while (properties.hasMoreElements()) {
Object propertyName = properties.nextElement();
Object propertyName = properties.nextElement();
// get the current value of the property
Object value = updatedProps.getProperty(propertyName.toString());
if (value != null) {
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.apache.hadoop.hdds.conf;

@ConfigGroup(prefix = "ozone.scm.client")
public class SimpleConfiguration {

private String clientAddress;

private String bindHost;

private boolean enabled;

private int port = 1234;

@Config(key = "address")
public void setClientAddress(String clientAddress) {
this.clientAddress = clientAddress;
}

@Config(key = "bind.host")
public void setBindHost(String bindHost) {
this.bindHost = bindHost;
}

@Config(key = "enabled")
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

@Config(key = "port")
public void setPort(int port) {
this.port = port;
}

public String getClientAddress() {
return clientAddress;
}

public String getBindHost() {
return bindHost;
}

public boolean isEnabled() {
return enabled;
}

public int getPort() {
return port;
}
}
Original file line number Diff line number Diff line change
@@ -97,6 +97,35 @@ public void testGetAllPropertiesByTags() throws Exception {
.getProperty("dfs.cblock.trace.io"));
}

@Test
public void getConfigurationObject() {
OzoneConfiguration conf = new OzoneConfiguration();
conf.set("ozone.scm.client.address", "address");
conf.set("ozone.scm.client.bind.host", "host");
conf.set("ozone.scm.client.enabled", "true");
conf.set("ozone.scm.client.port", "5555");

SimpleConfiguration configuration =
conf.getObject(SimpleConfiguration.class);

Assert.assertEquals("host", configuration.getBindHost());
Assert.assertEquals("address", configuration.getClientAddress());
Assert.assertEquals(true, configuration.isEnabled());
Assert.assertEquals(5555, configuration.getPort());
}

@Test
public void getConfigurationObjectWithDefault() {
OzoneConfiguration conf = new OzoneConfiguration();

SimpleConfiguration configuration =
conf.getObject(SimpleConfiguration.class);

Assert.assertEquals(false, configuration.isEnabled());
Assert.assertEquals(9860, configuration.getPort());
}


private void appendProperty(BufferedWriter out, String name, String val)
throws IOException {
this.appendProperty(out, name, val, false);
Loading