001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.jbi.monitoring;
018    
019    import java.util.Iterator;
020    import java.util.Timer;
021    import java.util.TimerTask;
022    import java.util.concurrent.ConcurrentHashMap;
023    
024    import javax.jbi.JBIException;
025    import javax.jbi.messaging.ExchangeStatus;
026    import javax.jbi.messaging.MessageExchange;
027    import javax.jbi.messaging.MessageExchange.Role;
028    import javax.jbi.servicedesc.ServiceEndpoint;
029    import javax.management.JMException;
030    import javax.management.MBeanAttributeInfo;
031    import javax.management.MBeanOperationInfo;
032    
033    import org.apache.commons.logging.Log;
034    import org.apache.commons.logging.LogFactory;
035    import org.apache.servicemix.JbiConstants;
036    import org.apache.servicemix.jbi.container.JBIContainer;
037    import org.apache.servicemix.jbi.event.ComponentAdapter;
038    import org.apache.servicemix.jbi.event.ComponentEvent;
039    import org.apache.servicemix.jbi.event.ComponentListener;
040    import org.apache.servicemix.jbi.event.EndpointAdapter;
041    import org.apache.servicemix.jbi.event.EndpointEvent;
042    import org.apache.servicemix.jbi.event.EndpointListener;
043    import org.apache.servicemix.jbi.event.ExchangeEvent;
044    import org.apache.servicemix.jbi.event.ExchangeListener;
045    import org.apache.servicemix.jbi.framework.ComponentMBeanImpl;
046    import org.apache.servicemix.jbi.management.AttributeInfoHelper;
047    import org.apache.servicemix.jbi.management.BaseSystemService;
048    import org.apache.servicemix.jbi.management.ManagementContext;
049    import org.apache.servicemix.jbi.management.OperationInfoHelper;
050    import org.apache.servicemix.jbi.messaging.MessageExchangeImpl;
051    import org.apache.servicemix.jbi.servicedesc.AbstractServiceEndpoint;
052    import org.apache.servicemix.jbi.servicedesc.EndpointSupport;
053    
054    /**
055     * 
056     * @author gnodet
057     * @org.apache.xbean.XBean element="statistics"
058     */
059    public class StatisticsService extends BaseSystemService implements StatisticsServiceMBean {
060    
061        private static final Log LOG = LogFactory.getLog(StatisticsService.class);
062        
063        private ConcurrentHashMap<String, ComponentStats> componentStats = new ConcurrentHashMap<String, ComponentStats>();
064        private ConcurrentHashMap<String, EndpointStats> endpointStats = new ConcurrentHashMap<String, EndpointStats>();
065        
066        private ComponentListener componentListener;
067        private EndpointListener endpointListener;
068        private ExchangeListener exchangeListener;
069        private boolean dumpStats = true;
070        private long statsInterval = 5;
071        private Timer statsTimer;
072        private TimerTask timerTask;
073    
074        /**
075         * @return the statsInterval
076         */
077        public long getStatsInterval() {
078            return statsInterval;
079        }
080    
081        /**
082         * @param statsInterval the statsInterval to set
083         */
084        public void setStatsInterval(long statsInterval) {
085            this.statsInterval = statsInterval;
086        }
087    
088        /**
089         * @return the dumpStats
090         */
091        public boolean isDumpStats() {
092            return dumpStats;
093        }
094    
095        /**
096         * @param dumpStats the dumpStats to set
097         */
098        public void setDumpStats(boolean value) {
099            if (dumpStats && !value) {
100                if (timerTask != null) {
101                    timerTask.cancel();
102                }
103            } else if (!dumpStats && value) {
104                dumpStats = value; //scheduleStatsTimer relies on dumpStats value
105                scheduleStatsTimer();
106            }
107            dumpStats = value;
108        }
109    
110        protected Class<StatisticsServiceMBean> getServiceMBean() {
111            return StatisticsServiceMBean.class;
112        }
113    
114        public String getDescription() {
115            return "EndpointStats service";
116        }
117        
118        public void resetAllStats() {
119            for (Iterator<ComponentStats> it = componentStats.values().iterator(); it.hasNext();) {
120                ComponentStats stats = it.next();
121                stats.reset();
122            }
123            for (Iterator<EndpointStats> it = endpointStats.values().iterator(); it.hasNext();) {
124                EndpointStats stats = it.next();
125                stats.reset();
126            }
127        }
128        
129        /* (non-Javadoc)
130         * @see javax.jbi.management.LifeCycleMBean#start()
131         */
132        public void start() throws javax.jbi.JBIException {
133            super.start();
134            this.container.addListener(exchangeListener);
135            if (isDumpStats()) {
136                scheduleStatsTimer();
137            }
138        }
139    
140        /* (non-Javadoc)
141         * @see javax.jbi.management.LifeCycleMBean#stop()
142         */
143        public void stop() throws javax.jbi.JBIException {
144            this.container.removeListener(exchangeListener);
145            super.stop();
146            for (Iterator<ComponentStats> it = componentStats.values().iterator(); it.hasNext();) {
147                ComponentStats stats = it.next();
148                stats.close();
149            }
150            if (timerTask != null) {
151                timerTask.cancel();
152            }
153            if (statsTimer != null) {
154                statsTimer.cancel();
155            }
156        }
157    
158        public void init(JBIContainer container) throws JBIException {
159            endpointListener = new EndpointAdapter() {
160                public void internalEndpointRegistered(EndpointEvent event) {
161                    onEndpointRegistered(event);
162                }
163                public void internalEndpointUnregistered(EndpointEvent event) {
164                    onEndpointUnregistered(event);
165                }
166                public void externalEndpointRegistered(EndpointEvent event) {
167                    onEndpointRegistered(event);
168                }
169                public void externalEndpointUnregistered(EndpointEvent event) {
170                    onEndpointUnregistered(event);
171                }
172            };
173            componentListener = new ComponentAdapter() {
174                public void componentInitialized(ComponentEvent event) {
175                    onComponentInitialized(event);
176                }
177                public void componentShutDown(ComponentEvent event) {
178                    onComponentShutDown(event);
179                }
180            };
181            exchangeListener = new ExchangeListener() {
182                public void exchangeSent(ExchangeEvent event) {
183                    onExchangeSent(event);
184                }
185                public void exchangeAccepted(ExchangeEvent event) {
186                    onExchangeAccepted(event);
187                }
188            };
189            container.addListener(componentListener);
190            container.addListener(endpointListener);
191            super.init(container);
192        }
193        
194        protected void onComponentInitialized(ComponentEvent event) {
195            ComponentMBeanImpl component = event.getComponent();
196            String key = component.getName();
197            ComponentStats stats = new ComponentStats(component);
198            componentStats.putIfAbsent(key, stats);
199            // Register MBean
200            ManagementContext context = container.getManagementContext();
201            try {
202                context.registerMBean(context.createObjectName(context.createObjectNameProps(stats, true)), 
203                        stats, 
204                        ComponentStatsMBean.class);
205            } catch (Exception e) {
206                LOG.info("Unable to register component statistics MBean: " + e.getMessage());
207                if (LOG.isDebugEnabled()) {
208                    LOG.debug("Unable to register component statistics MBean", e);
209                }
210            }
211        }
212        
213        protected void onComponentShutDown(ComponentEvent event) {
214            ComponentMBeanImpl component = event.getComponent();
215            String key = component.getName();
216            ComponentStats stats = componentStats.remove(key);
217            if (stats == null) {
218                return;
219            }
220            // Register MBean
221            ManagementContext context = container.getManagementContext();
222            try {
223                context.unregisterMBean(context.createObjectName(context.createObjectNameProps(stats, true)));
224            } catch (Exception e) {
225                LOG.info("Unable to unregister component statistics MBean: " + e);
226                if (LOG.isDebugEnabled()) {
227                    LOG.debug("Unable to unregister component statistics MBean", e);
228                }
229            }
230        }
231        
232        protected void onEndpointRegistered(EndpointEvent event) {
233            AbstractServiceEndpoint endpoint = (AbstractServiceEndpoint) event.getEndpoint();
234            String key = EndpointSupport.getUniqueKey(endpoint);
235            ComponentStats compStats = componentStats.get(endpoint.getComponentNameSpace().getName()); 
236            EndpointStats stats = new EndpointStats(endpoint, compStats.getMessagingStats());
237            endpointStats.putIfAbsent(key, stats);
238            // Register MBean
239            ManagementContext context = container.getManagementContext();
240            try {
241                context.registerMBean(context.createObjectName(context.createObjectNameProps(stats, true)), 
242                                      stats, 
243                                      EndpointStatsMBean.class);
244            } catch (Exception e) {
245                LOG.info("Unable to register endpoint statistics MBean: " + e.getMessage());
246                if (LOG.isDebugEnabled()) {
247                    LOG.debug("Unable to register endpoint statistics MBean", e);
248                }
249            }
250        }
251        
252        protected void onEndpointUnregistered(EndpointEvent event) {
253            AbstractServiceEndpoint endpoint = (AbstractServiceEndpoint) event.getEndpoint();
254            String key = EndpointSupport.getUniqueKey(endpoint);
255            EndpointStats stats = endpointStats.remove(key);
256            // Register MBean
257            ManagementContext context = container.getManagementContext();
258            try {
259                context.unregisterMBean(context.createObjectName(context.createObjectNameProps(stats, true)));
260            } catch (Exception e) {
261                LOG.info("Unable to unregister endpoint statistics MBean: " + e.getMessage());
262                if (LOG.isDebugEnabled()) {
263                    LOG.debug("Unable to unregister endpoint statistics MBean", e);
264                }
265            }
266        }
267    
268        protected void onExchangeSent(ExchangeEvent event) {
269            MessageExchange me = event.getExchange();
270            // This is a new exchange sent by a consumer
271            if (me.getStatus() == ExchangeStatus.ACTIVE
272                    && me.getRole() == Role.CONSUMER 
273                    && me.getMessage("out") == null 
274                    && me.getFault() == null
275                    && me instanceof MessageExchangeImpl) {
276                MessageExchangeImpl mei = (MessageExchangeImpl) me;
277                String source = (String) me.getProperty(JbiConstants.SENDER_ENDPOINT);
278                if (source == null) {
279                    source = mei.getSourceId().getName();
280                    ComponentStats stats = componentStats.get(source);
281                    stats.incrementOutbound();
282                } else {
283                    ServiceEndpoint[] ses = getContainer().getRegistry().getEndpointRegistry()
284                                                    .getAllEndpointsForComponent(mei.getSourceId());
285                    for (int i = 0; i < ses.length; i++) {
286                        if (EndpointSupport.getKey(ses[i]).equals(source)) {
287                            source = EndpointSupport.getUniqueKey(ses[i]);
288                            EndpointStats stats = endpointStats.get(source);
289                            stats.incrementOutbound();
290                            break;
291                        }
292                    }
293                }
294            }
295        }
296        
297        protected void onExchangeAccepted(ExchangeEvent event) {
298            MessageExchange me = event.getExchange();
299            // This is a new exchange sent by a consumer
300            if (me.getStatus() == ExchangeStatus.ACTIVE
301                    && me.getRole() == Role.PROVIDER 
302                    && me.getMessage("out") == null 
303                    && me.getFault() == null
304                    && me instanceof MessageExchangeImpl) {
305                String source = EndpointSupport.getUniqueKey(me.getEndpoint());
306                EndpointStats stats = endpointStats.get(source);
307                stats.incrementInbound();
308            }        
309        }
310        
311        protected void scheduleStatsTimer() {
312            if (statsTimer == null) {
313                statsTimer = new Timer(true);
314            }
315            if (timerTask != null) {
316                timerTask.cancel();
317            }
318            timerTask = new TimerTask() {
319                public void run() {
320                    doDumpStats();
321                }
322            };
323            long interval = statsInterval * 1000;
324            statsTimer.scheduleAtFixedRate(timerTask, interval, interval);
325        }
326        
327        protected void doDumpStats() {
328            for (Iterator<ComponentStats> it = componentStats.values().iterator(); it.hasNext();) {
329                ComponentStats stats = it.next();
330                stats.dumpStats();
331            }
332        }
333    
334        /**
335         * Get an array of MBeanAttributeInfo
336         * 
337         * @return array of AttributeInfos
338         * @throws JMException
339         */
340        public MBeanAttributeInfo[] getAttributeInfos() throws JMException {
341            AttributeInfoHelper helper = new AttributeInfoHelper();
342            helper.addAttribute(getObjectToManage(), "dumpStats", "Periodically dump Component statistics");
343            helper.addAttribute(getObjectToManage(), "statsInterval", "Interval (secs) before dumping statistics");
344            return AttributeInfoHelper.join(super.getAttributeInfos(), helper.getAttributeInfos());
345        }
346    
347        /**
348         * Get an array of MBeanOperationInfo
349         * 
350         * @return array of OperationInfos
351         */
352        public MBeanOperationInfo[] getOperationInfos() throws JMException {
353            OperationInfoHelper helper = new OperationInfoHelper();
354            helper.addOperation(getObjectToManage(), "resetAllStats", "reset all statistics");
355            return OperationInfoHelper.join(super.getOperationInfos(), helper.getOperationInfos());
356        }
357    
358    }