/** * 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.hadoop.gateway.config.impl; import org.apache.commons.beanutils.ConvertUtilsBean2; import org.apache.hadoop.gateway.config.*; import org.apache.hadoop.gateway.config.spi.ConfigurationInjector; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; 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(); 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, binding ); if( value == null ) { Optional optional = field.getAnnotation( Optional.class ); if( optional == null ) { throw new ConfigurationException( String.format( "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( "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, binding ); 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( "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( "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 = null; try { objValue = DEFAULT_CONVERTER.convert( strValue, type ); } catch( Exception e ) { throw new ConfigurationException( String.format( "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, ConfigurationBinding binding ) { Object value; try { value = adapter.getConfigurationValue( bind ); } catch( Exception e ) { throw new ConfigurationException( String.format( "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.getClass() ) ) { 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.length() > 0 ) { name = tagValue; } } return name; } private static String getBindName( Object target, String name, ConfigurationBinding binding ) { String bind = null; try { bind = binding.getConfigurationName( name ); } catch( Exception e ) { throw new ConfigurationException( String.format( "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 != null && tag instanceof Alias ) { Alias aliasTag = Alias.class.cast( tag ); String aliasValue = aliasTag.value().trim(); if( aliasValue.length() > 0 ) { 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; } }