001    // Copyright 2004, 2005 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.beans.BeanInfo;
018    import java.beans.EventSetDescriptor;
019    import java.beans.IntrospectionException;
020    import java.beans.Introspector;
021    import java.lang.reflect.Method;
022    import java.util.HashMap;
023    import java.util.Map;
024    
025    import org.apache.hivemind.ErrorLog;
026    import org.apache.hivemind.HiveMind;
027    import org.apache.hivemind.Location;
028    import org.apache.hivemind.impl.BaseLocatable;
029    import org.apache.hivemind.service.EventLinker;
030    
031    /**
032     * Implementation of {@link org.apache.hivemind.service.EventLinker}. Will output warnings whenever
033     * a consumer can't be registered for at least one event set (which can happen when the consumer
034     * does not implement the necessary interfaces).
035     * 
036     * @author Howard Lewis Ship
037     */
038    public class EventLinkerImpl extends BaseLocatable implements EventLinker
039    {
040        private ErrorLog _errorLog;
041    
042        /**
043         * Map of {@link java.beans.EventSetDescriptor}[], keyed on producer class.
044         */
045        private Map _producerEventSets;
046    
047        public EventLinkerImpl(ErrorLog errorLog)
048        {
049            _errorLog = errorLog;
050        }
051    
052        public void addEventListener(Object producer, String eventSetName, Object consumer,
053                Location location)
054        {
055            EventSetDescriptor[] sets = getEventSets(producer);
056            boolean nameMatch = HiveMind.isNonBlank(eventSetName);
057            Class consumerClass = consumer.getClass();
058    
059            int count = 0;
060            for (int i = 0; i < sets.length; i++)
061            {
062                EventSetDescriptor set = sets[i];
063                String name = set.getName();
064    
065                if (nameMatch)
066                {
067                    if (!eventSetName.equals(name))
068                        continue;
069    
070                    if (isAssignable(set, consumerClass))
071                        addEventListener(producer, set, consumer, location);
072                    else
073                    {
074                        _errorLog.error(
075                                ServiceMessages.notCompatibleWithEvent(consumer, set, producer),
076                                location,
077                                null);
078                    }
079    
080                    return;
081                }
082    
083                // Not matching on name, add anything that fits!
084    
085                if (isAssignable(set, consumerClass))
086                {
087                    addEventListener(producer, set, consumer, location);
088                    count++;
089                }
090            }
091    
092            if (count == 0)
093            {
094                if (nameMatch)
095                    _errorLog.error(
096                            ServiceMessages.noSuchEventSet(producer, eventSetName),
097                            location,
098                            null);
099                else
100                    _errorLog.error(ServiceMessages.noEventMatches(consumer, producer), location, null);
101            }
102        }
103    
104        private boolean isAssignable(EventSetDescriptor set, Class consumerClass)
105        {
106            return set.getListenerType().isAssignableFrom(consumerClass);
107        }
108    
109        private void addEventListener(Object producer, EventSetDescriptor set, Object consumer,
110                Location location)
111        {
112            Method m = set.getAddListenerMethod();
113    
114            try
115            {
116                m.invoke(producer, new Object[]
117                { consumer });
118            }
119            catch (Exception ex)
120            {
121                _errorLog.error(ServiceMessages.unableToAddListener(
122                        producer,
123                        set,
124                        consumer,
125                        location,
126                        ex), location, ex);
127    
128            }
129        }
130    
131        private EventSetDescriptor[] getEventSets(Object producer)
132        {
133            return getEventSets(producer.getClass());
134        }
135    
136        private synchronized EventSetDescriptor[] getEventSets(Class producerClass)
137        {
138            EventSetDescriptor[] result = null;
139    
140            if (_producerEventSets == null)
141                _producerEventSets = new HashMap();
142            else
143                result = (EventSetDescriptor[]) _producerEventSets.get(producerClass);
144    
145            if (result == null)
146            {
147                result = findEventSets(producerClass);
148    
149                _producerEventSets.put(producerClass, result);
150            }
151    
152            return result;
153        }
154    
155        private EventSetDescriptor[] findEventSets(Class producerClass)
156        {
157            synchronized (HiveMind.INTROSPECTOR_MUTEX)
158            {
159                try
160                {
161                    BeanInfo beanInfo = Introspector.getBeanInfo(producerClass);
162    
163                    // Will return an empty array (not null) when the class contains
164                    // no event sets.
165    
166                    return beanInfo.getEventSetDescriptors();
167                }
168                catch (IntrospectionException ex)
169                {
170                    _errorLog.error(
171                            ServiceMessages.unableToIntrospectClass(producerClass, ex),
172                            null,
173                            ex);
174    
175                    return new EventSetDescriptor[0];
176                }
177            }
178        }
179    
180    }