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 }