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