blob: a138d856f7722e025d303b8807eda04c3999f462 [file] [log] [blame]
/*
* 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
*
* 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.apache.knox.gateway.config.impl;
import org.apache.commons.beanutils.ConvertUtilsBean2;
import org.apache.knox.gateway.config.Alias;
import org.apache.knox.gateway.config.ConfigurationAdapter;
import org.apache.knox.gateway.config.ConfigurationBinding;
import org.apache.knox.gateway.config.ConfigurationException;
import org.apache.knox.gateway.config.Configure;
import org.apache.knox.gateway.config.Default;
import org.apache.knox.gateway.config.Optional;
import org.apache.knox.gateway.config.spi.ConfigurationInjector;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
public class DefaultConfigurationInjector implements ConfigurationInjector {
private static ConvertUtilsBean2 DEFAULT_CONVERTER = new ConvertUtilsBean2();
@Override
public void configure( Object target, ConfigurationAdapter adapter, ConfigurationBinding binding )
throws ConfigurationException {
Class type = target.getClass();
while( type != null ) {
injectClass( type, target, adapter, binding );
type = type.getSuperclass();
}
}
private void injectClass( Class type, Object target, ConfigurationAdapter config, ConfigurationBinding binding )
throws ConfigurationException {
Field[] fields = type.getDeclaredFields();
Arrays.sort(fields, new Comparator<Field>() {
@Override
public int compare(Field field1, Field field2) {
if ("serviceRole".equals(field1.getName())) {
return -1;
} else if ("serviceRole".equals(field2.getName())) {
return 1;
} else {
return field1.getName().compareTo(field2.getName());
}
};
});
for( Field field : fields ) {
injectFieldValue( field, target, config, binding );
}
Method[] methods = type.getDeclaredMethods();
for( Method method : methods ) {
injectMethodValue( method, target, config, binding );
}
}
private void injectFieldValue( Field field, Object target, ConfigurationAdapter adapter, ConfigurationBinding binding )
throws ConfigurationException {
Configure annotation = field.getAnnotation( Configure.class );
if( annotation != null ) {
Alias alias = field.getAnnotation( Alias.class );
String name = getConfigName( field, alias );
String bind = getBindName( target, name, binding );
Object value = retrieveValue( target, bind, name, field.getType(), adapter );
if( value == null ) {
Optional optional = field.getAnnotation( Optional.class );
if( optional == null ) {
throw new ConfigurationException( String.format( Locale.ROOT,
"Failed to find configuration for %s bound to %s of %s via %s",
bind, name, target.getClass().getName(), adapter.getClass().getName() ) );
}
} else {
try {
if( !field.isAccessible() ) {
field.setAccessible( true );
}
field.set( target, value );
} catch( Exception e ) {
throw new ConfigurationException( String.format( Locale.ROOT,
"Failed to inject field configuration property %s of %s",
name, target.getClass().getName() ), e );
}
}
}
}
private void injectMethodValue( Method method, Object target, ConfigurationAdapter adapter, ConfigurationBinding binding )
throws ConfigurationException {
Configure methodTag = method.getAnnotation( Configure.class );
if( methodTag != null ) {
Alias aliasTag = method.getAnnotation( Alias.class );
String methodName = getConfigName( method, aliasTag );
Class[] argTypes = method.getParameterTypes();
Object[] args = new Object[ argTypes.length ];
Annotation[][] argTags = method.getParameterAnnotations();
for( int i=0; i<argTypes.length; i++ ) {
String argName = getConfigName( methodName, argTags[i] );
String bndName = getBindName( target, argName, binding );
Object argValue = retrieveValue( target, bndName, argName, argTypes[i], adapter );
if( argValue == null ) {
Default defTag = findAnnotation( argTags[i], Default.class );
if( defTag != null ) {
String strValue = defTag.value();
argValue = convertValue( target, argName, strValue, argTypes[i] );
} else {
throw new ConfigurationException( String.format( Locale.ROOT,
"Failed to find configuration for %s as %s of %s via %s",
bndName, argName, target.getClass().getName(), adapter.getClass().getName() ) );
}
}
args[ i ] = argValue;
}
if( !method.isAccessible() ) {
method.setAccessible( true );
}
try {
method.invoke( target, args );
} catch( Exception e ) {
throw new ConfigurationException( String.format( Locale.ROOT,
"Failed to inject method configuration via %s of %s",
methodName, target.getClass().getName() ), e );
}
}
}
private Object convertValue( Object target, String name, Object strValue, Class<?> type ) {
Object objValue;
try {
objValue = DEFAULT_CONVERTER.convert( strValue, type );
} catch( Exception e ) {
throw new ConfigurationException( String.format( Locale.ROOT,
"Failed to convert configuration for %s of %s to %s",
name, target.getClass().getName(), type.getName() ), e );
}
return objValue;
}
private Object retrieveValue( Object target, String bind, String name, Class<?> type, ConfigurationAdapter adapter) {
Object value;
try {
value = adapter.getConfigurationValue( bind );
} catch( Exception e ) {
throw new ConfigurationException( String.format( Locale.ROOT,
"Failed to retrieve configuration for %s bound to %s of %s via %s",
bind, name, target.getClass().getName(), adapter.getClass().getName() ), e );
}
// Otherwise null sometimes ends up being converted to 0.
if( value != null ) {
value = convertValue( target, name, value, type );
}
return value;
}
private <T extends Annotation> T findAnnotation( Annotation[] annotations, Class<T> type ) {
T found = null;
for( Annotation current : annotations ) {
if( type.isAssignableFrom( current.annotationType() ) ) {
found = (T)current;
break;
}
}
return found;
}
private static String pickName( String implied, Alias explicit ) {
String name = implied;
if( explicit != null ) {
String tagValue = explicit.value().trim();
if(!tagValue.isEmpty()) {
name = tagValue;
}
}
return name;
}
private static String getBindName( Object target, String name, ConfigurationBinding binding ) {
String bind;
try {
bind = binding.getConfigurationName( name );
} catch( Exception e ) {
throw new ConfigurationException( String.format( Locale.ROOT,
"Failed to bind configuration for %s of %s via %s",
name, target.getClass().getName(), binding.getClass().getName() ), e );
}
if( bind == null ) {
bind = name;
}
return bind;
}
private static String getConfigName( Field field, Alias tag ) {
return pickName( field.getName(), tag );
}
private static String getConfigName( String name, Annotation[] tags ) {
if( tags != null ) {
for( Annotation tag : tags ) {
if(tag instanceof Alias) {
Alias aliasTag = (Alias) tag;
String aliasValue = aliasTag.value().trim();
if(!aliasValue.isEmpty()) {
name = aliasValue;
break;
}
}
}
}
return name;
}
private static String getConfigName( Method method, Alias tag ) {
return pickName( getConfigName( method ), tag );
}
private static String getConfigName( Method method ) {
String methodName = method.getName();
if( methodName != null ) {
StringBuilder name = new StringBuilder( methodName.length() );
if( methodName.length() > 3 &&
methodName.startsWith( "set" ) &&
Character.isUpperCase( methodName.charAt( 3 ) ) ) {
name.append( methodName.substring( 3 ) );
name.setCharAt( 0, Character.toLowerCase( name.charAt( 0 ) ) );
} else {
name.append( name );
}
return name.toString();
}
return null;
}
}