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    }