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 }