001 // Copyright 2007 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.hivemind.service.impl;
016
017 import java.util.HashSet;
018 import java.util.Iterator;
019 import java.util.List;
020 import java.util.Map;
021 import java.util.Set;
022 import java.util.TreeMap;
023
024 import org.apache.commons.logging.Log;
025 import org.apache.commons.logging.LogFactory;
026 import org.apache.hivemind.ApplicationRuntimeException;
027 import org.apache.hivemind.ErrorHandler;
028 import org.apache.hivemind.internal.RegistryInfrastructure;
029 import org.apache.hivemind.service.Autowiring;
030 import org.apache.hivemind.service.AutowiringStrategy;
031 import org.apache.hivemind.util.PropertyUtils;
032
033 /**
034 * Implementation of {@link Autowiring}.
035 * Skips properties of primitive type and the standard java data types (String, Double etc.).
036 * Properties that are already assigned are skipped too.
037 * Delegates the autowiring to implementations of {@link AutowiringStrategy}.
038 * If errors occur they are passed to an {@link ErrorHandler}. Depending on its
039 * implementation the wiring either continues with the next property or an exception
040 * is thrown.
041 *
042 * @author Achim Huegen
043 */
044 public class AutowiringImpl implements Autowiring
045 {
046 private static final Log LOG = LogFactory.getLog(AutowiringImpl.class);
047
048 private Map _strategies = new TreeMap();
049
050 private RegistryInfrastructure _registry;
051
052 private ErrorHandler _errorHandler;
053
054 private static Set SKIPPED_PROPERTY_TYPES = new HashSet();
055
056 static {
057 SKIPPED_PROPERTY_TYPES.add(String.class);
058 SKIPPED_PROPERTY_TYPES.add(Double.class);
059 SKIPPED_PROPERTY_TYPES.add(Integer.class);
060 SKIPPED_PROPERTY_TYPES.add(Float.class);
061 SKIPPED_PROPERTY_TYPES.add(Byte.class);
062 SKIPPED_PROPERTY_TYPES.add(Short.class);
063 SKIPPED_PROPERTY_TYPES.add(Character.class);
064 }
065
066 /**
067 * @param registry reference to registry
068 * @param strategyContributions list with instances of {@link AutowiringStrategyContribution}
069 * @param errorHandler handler for dealing with recoverable errors.
070 */
071 public AutowiringImpl(RegistryInfrastructure registry, List strategyContributions, ErrorHandler errorHandler)
072 {
073 _registry = registry;
074 _errorHandler = errorHandler;
075 // Add the strategies in default order to map
076 for (Iterator iter = strategyContributions.iterator(); iter.hasNext();)
077 {
078 AutowiringStrategyContribution c = (AutowiringStrategyContribution) iter.next();
079 _strategies.put(c.getName(), c.getStrategy());
080 }
081 }
082
083 /**
084 * @see org.apache.hivemind.service.Autowiring#autowireProperties(java.lang.Object)
085 */
086 public Object autowireProperties(Object target)
087 {
088 Set writeablePropertiesSet = new HashSet(PropertyUtils.getWriteableProperties(target));
089 String[] writableProperties = (String[]) writeablePropertiesSet.toArray(new String[writeablePropertiesSet.size()]);
090 return autowireProperties(target, writableProperties);
091 }
092
093 /**
094 * @see org.apache.hivemind.service.Autowiring#autowireProperties(java.lang.Object, java.lang.String[])
095 */
096 public Object autowireProperties(Object target, String[] propertyNames)
097 {
098 for (int i = 0; i < propertyNames.length; i++)
099 {
100 String propertyName = propertyNames[i];
101 if (isPropertyWirable(target, propertyName)) {
102 autowirePropertyAllStrategies(target, propertyName);
103 }
104 }
105 return target;
106 }
107
108 /**
109 * Wires a single property by calling all available strategies in the configured order
110 * until a strategy signals that the property has been wired.
111 *
112 * @param target
113 * @param propertyName
114 */
115 private void autowirePropertyAllStrategies(Object target, String propertyName)
116 {
117 try
118 {
119 for (Iterator iter = _strategies.values().iterator(); iter.hasNext();)
120 {
121 AutowiringStrategy strategy = (AutowiringStrategy) iter.next();
122 boolean isWired = strategy.autowireProperty(_registry, target, propertyName);
123 // Stop if strategy has wired the property
124 if (isWired)
125 break;
126 }
127 }
128 catch (Exception ex)
129 {
130 _errorHandler.error(
131 LOG,
132 ServiceMessages.autowirePropertyFailure(propertyName, target.getClass(), ex),
133 null,
134 ex);
135 }
136 }
137
138 /**
139 * @see org.apache.hivemind.service.Autowiring#autowireProperties(java.lang.String, java.lang.Object)
140 */
141 public Object autowireProperties(String strategy, Object target)
142 {
143 Set writeablePropertiesSet = new HashSet(PropertyUtils.getWriteableProperties(target));
144 String[] writableProperties = (String[]) writeablePropertiesSet.toArray(new String[writeablePropertiesSet.size()]);
145 return autowireProperties(strategy, target, writableProperties);
146 }
147
148 /**
149 * @see org.apache.hivemind.service.Autowiring#autowireProperties(java.lang.String, java.lang.Object, java.lang.String[])
150 */
151 public Object autowireProperties(String strategyName, Object target, String[] propertyNames)
152 {
153 for (int i = 0; i < propertyNames.length; i++)
154 {
155 String propertyName = propertyNames[i];
156 if (isPropertyWirable(target, propertyName)) {
157 autowireProperty(strategyName, target, propertyName);
158 }
159 }
160 return target;
161 }
162
163 /**
164 * @return true if the property is wirable. Primitive types and the types in
165 * SKIPPED_PROPERTY_TYPES are ignored. If the property is already assigned it is
166 * ignored too.
167 */
168 private boolean isPropertyWirable(Object target, String propertyName)
169 {
170 Class propertyType = PropertyUtils.getPropertyType(target, propertyName);
171 if (propertyType.isPrimitive() || SKIPPED_PROPERTY_TYPES.contains(propertyType)) {
172 return false;
173 } else {
174 // Don't wire if value is already assigned
175 if (PropertyUtils.isReadable(target, propertyName))
176 return PropertyUtils.read(target, propertyName) == null;
177 else return true;
178 }
179 }
180
181 /**
182 * Wires a single property by using the strategy <code>strategyName</code>
183 * @param strategyName
184 * @param target
185 * @param propertyName
186 * @return true if wiring succeeded
187 */
188 private boolean autowireProperty(String strategyName, Object target, String propertyName)
189 {
190 try
191 {
192 AutowiringStrategy strategy = (AutowiringStrategy) _strategies.get(strategyName);
193 if (strategy == null) {
194 throw new ApplicationRuntimeException(ServiceMessages.unknownStrategy(strategyName));
195 }
196 return strategy.autowireProperty(_registry, target, propertyName);
197 }
198 catch (Exception ex)
199 {
200 _errorHandler.error(
201 LOG,
202 ServiceMessages.autowirePropertyFailure(propertyName, target.getClass(), ex),
203 null,
204 ex);
205 }
206 return false;
207 }
208
209 }