/* * Copyright (c) 2013 GigaSpaces Technologies Ltd. All rights reserved * * 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 beans.config; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.reflections.ReflectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import play.Configuration; import play.Play; import play.libs.Time; import com.google.common.base.Predicates; import com.typesafe.config.ConfigList; import com.typesafe.config.ConfigObject; import com.typesafe.config.ConfigValue; /** * User: guym * Date: 12/13/12 * Time: 1:33 PM * <p/> * this class will auto wire pojos to configuration. * I am currently using reflection and play configuration only. * There is no need for more */ public class ConfigBean { private static Logger logger = LoggerFactory.getLogger( ConfigBean.class ); private static Map<Class, ConfigValueHandler> handlers = new HashMap<Class, ConfigValueHandler>(); static { handlers.put( Integer.class, new IntegerHandler() ); handlers.put( int.class, new IntegerHandler() ); handlers.put( Boolean.class, new BooleanObjectHandler() ); handlers.put( boolean.class, new BooleanHandler() ); handlers.put( long.class, new TimeHandler() ); handlers.put( Long.class, new TimeHandler() ); handlers.put( String.class, new StringHandler() ); handlers.put( File.class, new FileHandler() ); } public Conf getConfiguration() { try{ logger.info("serializing configuration"); Conf root = new Conf(); injectConfiguration( root, Play.application().configuration() ); return root; }catch(RuntimeException e){ logger.error("unable to get configuration",e); throw e; } // try{ // logger.info( "getting configuration : {}" , Json.stringify( Json.toJson( root ) )); // logger.debug( System.getProperty( "java.class.path" ) ); // }catch(RuntimeException e){ } } private abstract static class ConfigValueHandler<T> { public abstract T getValue( Configuration conf, String key ); } private static class IntegerHandler extends ConfigValueHandler<Integer> { @Override public Integer getValue( Configuration conf, String key ) { return conf.getInt( key ); } } private static class BooleanHandler extends ConfigValueHandler<Boolean> { @Override public Boolean getValue( Configuration conf, String key ) { Boolean result = conf.getBoolean( key ); return result ; } } private static class BooleanObjectHandler extends ConfigValueHandler<Boolean> { @Override public Boolean getValue( Configuration conf, String key ) { return conf.getBoolean( key ); } } public static class StringHandler extends ConfigValueHandler<String> { @Override public String getValue( Configuration conf, String key ) { return conf.getString( key ); } } public static class FileHandler extends ConfigValueHandler<File> { @Override public File getValue( Configuration conf, String key ) { String string = conf.getString(key); if ( string == null ){ return null; } File file = Play.application().getFile(string); if ( !file.exists() ) { logger.warn( "file {} does not exists but required by the configuration", file.getAbsolutePath() ); } return file; } } public static class TimeHandler extends ConfigValueHandler<Long> { @Override public Long getValue( Configuration conf, String key ) { return Time.parseDuration( conf.getString( key ) ) * 1000L; } } private boolean isIgnoreNullValue( Config configAnn ){ return configAnn == null || configAnn.ignoreNullValues(); } private void injectConfiguration( Object obj, Configuration conf ) { Set<Field> allFields = ReflectionUtils.getAllFields( obj.getClass(), Predicates.alwaysTrue() ); for ( Field field : allFields ) { logger.info("injecting configuration for : " + field.getDeclaringClass().getName() + "#" + field.getName() ); String configKey = field.getName(); Config configAnn = null; if ( field.isAnnotationPresent( Config.class ) ) { configAnn = field.getAnnotation( Config.class ); String playKey = configAnn.playKey(); // use the annotated information only if not empty. configKey = StringUtils.isEmpty( playKey ) ? configKey : playKey; } if ( handlers.containsKey( field.getType() ) ) { try { if ( conf.getString( configKey ) != null || !isIgnoreNullValue( configAnn ) ){ Object value = handlers.get( field.getType() ).getValue( conf, configKey ); field.set( obj, value ); } } catch ( Exception e ) { logger.error( String.format("unable to set value for field [%s.%s]", field.getDeclaringClass().getName() , field.getName()) ,e ); } } else { // this is probably an Object. need to instantiate try { if ( field.getType().isEnum() ){ String configValue = conf.getString(configKey); if ( StringUtils.isNotEmpty(configValue) ){ field.set(obj, Enum.valueOf( (Class<? extends Enum>) field.getType(), configValue)); } } else if ( List.class.isAssignableFrom(field.getType()) ){ Field confField = conf.getClass().getDeclaredField("conf"); confField.setAccessible(true); play.api.Configuration innerConf = ( play.api.Configuration ) confField.get(conf); ConfigList configValues = innerConf.underlying().getList( field.getName() ); // get the actual type Type type = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; List fieldValue = new LinkedList(); for (ConfigValue configValue : configValues) { Object listItem = ((Class)type).newInstance(); injectConfiguration( listItem, new Configuration( new play.api.Configuration( ((ConfigObject)configValue).toConfig() ))); fieldValue.add(listItem); } field.set( obj, fieldValue ); } else if ( conf.getConfig( configKey ) != null ) { // important : we assume the field is not null. // this way we will be able to refresh configuration on command. Object value = field.get( obj ); injectConfiguration( value, conf.getConfig( configKey ) ); } } catch ( Exception e ) { throw new RuntimeException( String.format( "unable to populate configuration for key %s.%s", obj.getClass(), field.getName()), e ); } } ConfigValueHandler handler = handlers.containsKey( field.getType() ) ? handlers.get( field.getType() ) : handlers.get( Configuration.class ); } } }