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 ((ExtendedPropertyEditor) propertyEditor).setFormat(format);
189 }
190 return propertyEditor;
191 }
192 }
|