AbstractConfigurationManager.java
001 /*
002  * Copyright 2008-2017 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *     http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package org.codehaus.griffon.runtime.core.configuration;
017 
018 import griffon.core.ApplicationEvent;
019 import griffon.core.Configuration;
020 import griffon.core.GriffonApplication;
021 import griffon.core.RunnableWithArgs;
022 import griffon.core.configuration.ConfigurationManager;
023 import griffon.core.configuration.Configured;
024 import griffon.core.editors.ExtendedPropertyEditor;
025 import griffon.core.editors.PropertyEditorResolver;
026 import griffon.util.GriffonClassUtils;
027 import org.slf4j.Logger;
028 import org.slf4j.LoggerFactory;
029 
030 import javax.annotation.Nonnull;
031 import javax.annotation.Nullable;
032 import javax.annotation.PostConstruct;
033 import javax.inject.Inject;
034 import java.beans.PropertyDescriptor;
035 import java.beans.PropertyEditor;
036 import java.lang.reflect.Field;
037 import java.lang.reflect.Method;
038 import java.util.LinkedHashMap;
039 import java.util.Map;
040 
041 import static griffon.core.editors.PropertyEditorResolver.findEditor;
042 import static griffon.util.GriffonNameUtils.isBlank;
043 import static java.lang.reflect.Modifier.isStatic;
044 import static java.util.Objects.requireNonNull;
045 
046 /**
047  @author Andres Almiray
048  @since 2.11.0
049  */
050 public abstract class AbstractConfigurationManager implements ConfigurationManager {
051     private static final Logger LOG = LoggerFactory.getLogger(AbstractConfigurationManager.class);
052 
053     private static final String ERROR_INSTANCE_NULL = "Argument 'instance' must not be null";
054     private static final String ERROR_TYPE_NULL = "Argument 'type' must not be null";
055     private static final String ERROR_VALUE_NULL = "Argument 'value' must not be null";
056 
057     @Inject
058     protected GriffonApplication application;
059 
060     @PostConstruct
061     private void initialize() {
062         this.application = requireNonNull(application, "Argument 'application' cannot ne null");
063 
064         application.getEventRouter().addEventListener(ApplicationEvent.NEW_INSTANCE.getName()new RunnableWithArgs() {
065             @Override
066             public void run(@Nullable Object... args) {
067                 Object instance = args[1];
068                 injectConfiguration(instance);
069             }
070         });
071     }
072 
073     protected void injectConfiguration(@Nonnull Object instance) {
074         requireNonNull(instance, ERROR_INSTANCE_NULL);
075 
076         Map<String, ConfigurationDescriptor> descriptors = new LinkedHashMap<>();
077         Class<?> klass = instance.getClass();
078         do {
079             harvestDescriptors(instance.getClass(), klass, instance, descriptors);
080             klass = klass.getSuperclass();
081         while (null != klass);
082 
083         doConfigurationInjection(instance, descriptors);
084     }
085 
086     protected void harvestDescriptors(@Nonnull Class<?> instanceClass, @Nonnull Class<?> currentClass, @Nonnull Object instance, @Nonnull Map<String, ConfigurationDescriptor> descriptors) {
087         PropertyDescriptor[] propertyDescriptors = GriffonClassUtils.getPropertyDescriptors(currentClass);
088         for (PropertyDescriptor pd : propertyDescriptors) {
089             Method writeMethod = pd.getWriteMethod();
090             if (null == writeMethod) { continue}
091             if (isStatic(writeMethod.getModifiers())) {
092                 continue;
093             }
094 
095             Configured annotation = writeMethod.getAnnotation(Configured.class);
096             if (null == annotation) { continue}
097 
098             String propertyName = pd.getName();
099             String configuration = annotation.configuration().trim();
100             String key = annotation.value();
101             String defaultValue = annotation.defaultValue();
102             String format = annotation.format();
103 
104             if (LOG.isDebugEnabled()) {
105                 LOG.debug("Property " + propertyName +
106                     " of instance " + instance +
107                     " [configuration='" + configuration +
108                     "', key='" + key +
109                     "', defaultValue='" + defaultValue +
110                     "', format='" + format +
111                     "'] is marked for configuration injection.");
112             }
113             descriptors.put(propertyName, new MethodConfigurationDescriptor(writeMethod, configuration, key, defaultValue, format));
114         }
115 
116         for (Field field : currentClass.getDeclaredFields()) {
117             if (field.isSynthetic() || isStatic(field.getModifiers()) || descriptors.containsKey(field.getName())) {
118                 continue;
119             }
120             final Configured annotation = field.getAnnotation(Configured.class);
121             if (null == annotation) { continue}
122 
123             Class<?> resolvedClass = field.getDeclaringClass();
124             String fqFieldName = resolvedClass.getName().replace('$''.'"." + field.getName();
125             String configuration = annotation.configuration().trim();
126             String key = annotation.value();
127             String defaultValue = annotation.defaultValue();
128             String format = annotation.format();
129 
130             if (LOG.isDebugEnabled()) {
131                 LOG.debug("Field " + fqFieldName +
132                     " of instance " + instance +
133                     " [configuration='" + configuration +
134                     "', key='" + key +
135                     "', defaultValue='" + defaultValue +
136                     "', format='" + format +
137                     "'] is marked for configuration injection.");
138             }
139 
140             descriptors.put(field.getName()new FieldConfigurationDescriptor(field, configuration, key, defaultValue, format));
141         }
142     }
143 
144     protected void doConfigurationInjection(@Nonnull Object instance, @Nonnull Map<String, ConfigurationDescriptor> descriptors) {
145         for (ConfigurationDescriptor descriptor : descriptors.values()) {
146             Object value = resolveConfiguration(descriptor.getConfiguration(), descriptor.getKey(), descriptor.getDefaultValue());
147             InjectionPoint injectionPoint = descriptor.asInjectionPoint();
148             if (!injectionPoint.getType().isAssignableFrom(value.getClass())) {
149                 value = convertValue(injectionPoint.getType(), value, descriptor.getFormat());
150             }
151             injectionPoint.setValue(instance, value);
152         }
153     }
154 
155     @Nonnull
156     protected Object resolveConfiguration(@Nonnull String name, @Nonnull String key, @Nonnull String defaultValue) {
157         Configuration configuration = getConfiguration();
158         if (!isBlank(name)) {
159             configuration = getConfiguration(name);
160         }
161 
162         if (configuration.containsKey(key)) {
163             return configuration.get(key);
164         else {
165             return defaultValue;
166         }
167     }
168 
169     @Nonnull
170     protected Object convertValue(@Nonnull Class<?> type, @Nonnull Object value, @Nullable String format) {
171         requireNonNull(type, ERROR_TYPE_NULL);
172         requireNonNull(value, ERROR_VALUE_NULL);
173         PropertyEditor propertyEditor = resolvePropertyEditor(type, format);
174         if (propertyEditor instanceof PropertyEditorResolver.NoopPropertyEditor) { return value; }
175         if (value instanceof CharSequence) {
176             propertyEditor.setAsText(String.valueOf(value));
177         else {
178             propertyEditor.setValue(value);
179         }
180         return propertyEditor.getValue();
181     }
182 
183     @Nonnull
184     protected PropertyEditor resolvePropertyEditor(@Nonnull Class<?> type, @Nullable String format) {
185         requireNonNull(type, ERROR_TYPE_NULL);
186         PropertyEditor propertyEditor = findEditor(type);
187         if (propertyEditor instanceof ExtendedPropertyEditor) {
188             ((ExtendedPropertyEditorpropertyEditor).setFormat(format);
189         }
190         return propertyEditor;
191     }
192 }