001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.mq.server.jmx;
023:
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.Map;
028: import java.util.StringTokenizer;
029:
030: import javax.management.Attribute;
031: import javax.management.MBeanServer;
032: import javax.management.MalformedObjectNameException;
033: import javax.management.ObjectName;
034: import javax.transaction.xa.Xid;
035:
036: import org.jboss.mq.MessageStatistics;
037: import org.jboss.mq.pm.PersistenceManager;
038: import org.jboss.mq.pm.TxManager;
039: import org.jboss.mq.server.BasicQueueParameters;
040: import org.jboss.mq.server.JMSDestinationManager;
041: import org.jboss.mq.server.JMSServerInterceptor;
042: import org.jboss.mq.server.MessageCache;
043: import org.jboss.mq.server.MessageCounter;
044: import org.jboss.mq.server.Receivers;
045: import org.jboss.mq.sm.StateManager;
046: import org.jboss.mx.util.MBeanProxyExt;
047: import org.jboss.system.ServiceControllerMBean;
048: import org.jboss.system.ServiceMBeanSupport;
049: import org.jboss.util.JBossStringBuilder;
050: import org.jboss.util.threadpool.BasicThreadPool;
051: import org.jboss.util.threadpool.ThreadPool;
052:
053: /**
054: * JMX MBean implementation for JBossMQ.
055: *
056: * @jmx:mbean extends="org.jboss.mq.server.jmx.InterceptorMBean"
057: * @author Vincent Sheffer (vsheffer@telkel.com)
058: * @author <a href="mailto:jplindfo@helsinki.fi">Juha Lindfors</a>
059: * @author <a href="hiram.chirino@jboss.org">Hiram Chirino</a>
060: * @version $Revision: 57198 $
061: */
062: public class DestinationManager extends InterceptorMBeanSupport
063: implements DestinationManagerMBean {
064: public String jndiBindLocation = "java:/JBossMQServer";
065:
066: /** A proxy to the service controller. */
067: private ServiceControllerMBean serviceController;
068: /** The JMX ObjectName of this service MBean */
069: private ObjectName mqService;
070: /** The JMS server implementation */
071: protected JMSDestinationManager jmsServer;
072: /** The JMX ObjectName of the configured persistence manager */
073: private ObjectName persistenceManager;
074: /** The JMX ObjectName of the configured state manager */
075: private ObjectName stateManager;
076: /** The JMX ObjectName of the message cache */
077: private ObjectName messageCache;
078: /** The temporary topic/queue parameters */
079: protected BasicQueueParameters tempParameters = new BasicQueueParameters();
080: /** Default thread pool */
081: private ObjectName threadPool;
082: /** Default expiry destination */
083: private ObjectName expiryDestination;
084:
085: /**
086: * @jmx:managed-attribute
087: * @return the count of active clients
088: */
089: public int getClientCount() {
090: if (jmsServer == null)
091: return 0;
092: return jmsServer.getClientCount();
093: }
094:
095: /**
096: * @jmx:managed-attribute
097: * @return a Map<ConnectionToken, ClientConsumer> of current clients
098: */
099: public Map getClients() {
100: if (jmsServer == null)
101: return null;
102: return jmsServer.getClients();
103: }
104:
105: /**
106: * Get the value of PersistenceManager.
107: *
108: * @jmx:managed-attribute
109: * @return value of PersistenceManager.
110: */
111: public ObjectName getPersistenceManager() {
112: return persistenceManager;
113: }
114:
115: /**
116: * Set the value of PersistenceManager.
117: *
118: * @jmx:managed-attribute
119: * @param v Value to assign to PersistenceManager.
120: */
121: public void setPersistenceManager(ObjectName objectName) {
122: this .persistenceManager = objectName;
123: }
124:
125: /**
126: * Get the value of StateManager.
127: *
128: * @jmx:managed-attribute
129: * @return value of StateManager.
130: */
131: public ObjectName getStateManager() {
132: return stateManager;
133: }
134:
135: /**
136: * Set the value of StateManager.
137: *
138: * @jmx:managed-attribute
139: * @param v Value to assign to StateManager.
140: */
141: public void setStateManager(ObjectName objectName) {
142: this .stateManager = objectName;
143: }
144:
145: /**
146: * Get the value of MessageCache.
147: *
148: * @jmx:managed-attribute
149: * @return value of MessageCache.
150: */
151: public ObjectName getMessageCache() {
152: return messageCache;
153: }
154:
155: /**
156: * Set the value of MessageCache.
157: *
158: * @jmx:managed-attribute
159: * @param v Value to assign to MessageCache.
160: */
161: public void setMessageCache(ObjectName objectName) {
162: this .messageCache = objectName;
163: }
164:
165: /**
166: * Retrieve the temporary topic/queue max depth
167: * @return the maximum depth
168: * @jmx:managed-attribute
169: */
170: public int getTemporaryMaxDepth() {
171: return tempParameters.maxDepth;
172: }
173:
174: /**
175: * Set the temporary topic/queue max depth
176: * @param depth the maximum depth
177: * @jmx:managed-attribute
178: */
179: public void setTemporaryMaxDepth(int depth) {
180: tempParameters.maxDepth = depth;
181: }
182:
183: /**
184: * Retrieve the temporary topic/queue in memory mode
185: * @return true for in memory
186: * @jmx:managed-attribute
187: */
188: public boolean getTemporaryInMemory() {
189: return tempParameters.inMemory;
190: }
191:
192: /**
193: * Set the temporary topic/queue in memory mode
194: * @param mode true for in memory
195: * @jmx:managed-attribute
196: */
197: public void setTemporaryInMemory(boolean mode) {
198: tempParameters.inMemory = mode;
199: }
200:
201: /**
202: * Get the receivers implemenetation
203: *
204: * @return the receivers implementation class
205: * @jmx:managed-attribute
206: */
207: public Class getReceiversImpl() {
208: return tempParameters.receiversImpl;
209: }
210:
211: /**
212: * Set the receivers implementation class
213: *
214: * @param clazz the receivers implementation class
215: * @jmx:managed-attribute
216: */
217: public void setReceiversImpl(Class clazz) {
218: if (clazz != null
219: && Receivers.class.isAssignableFrom(clazz) == false)
220: throw new IllegalArgumentException("Class "
221: + clazz.getName()
222: + " is not a Receivers implementation");
223: tempParameters.receiversImpl = clazz;
224: }
225:
226: public int getRecoveryRetries() {
227: return tempParameters.recoveryRetries;
228: }
229:
230: public void setRecoveryRetries(int retries) {
231: tempParameters.recoveryRetries = retries;
232: }
233:
234: public ObjectName getThreadPool() {
235: return this .threadPool;
236: }
237:
238: public void setThreadPool(ObjectName threadPool) {
239: this .threadPool = threadPool;
240: }
241:
242: public ObjectName getExpiryDestination() {
243: return expiryDestination;
244: }
245:
246: public void setExpiryDestination(ObjectName expiryDestination) {
247: this .expiryDestination = expiryDestination;
248: }
249:
250: /**
251: * @jmx:managed-operation
252: */
253: public void createQueue(String name) throws Exception {
254: createDestination("org.jboss.mq.server.jmx.Queue",
255: getQueueObjectName(name), null);
256: }
257:
258: /**
259: * @jmx:managed-operation
260: */
261: public void createTopic(String name) throws Exception {
262: createDestination("org.jboss.mq.server.jmx.Topic",
263: getTopicObjectName(name), null);
264: }
265:
266: /**
267: * @jmx:managed-operation
268: */
269: public void createQueue(String name, String jndiLocation)
270: throws Exception {
271: createDestination("org.jboss.mq.server.jmx.Queue",
272: getQueueObjectName(name), jndiLocation);
273: }
274:
275: /**
276: * @jmx:managed-operation
277: */
278: public void createTopic(String name, String jndiLocation)
279: throws Exception {
280: createDestination("org.jboss.mq.server.jmx.Topic",
281: getTopicObjectName(name), jndiLocation);
282: }
283:
284: // TODO. Should we add any Kind of security configuration for these
285: // dynamicly created destination. For example en optional URL to
286: // an xml config file.
287: protected void createDestination(String type, ObjectName name,
288: String jndiLocation) throws Exception {
289: log.debug("Attempting to create destination: " + name
290: + "; type=" + type);
291:
292: server.createMBean(type, name);
293: server.setAttribute(name, new Attribute("DestinationManager",
294: mqService));
295: if (jndiLocation != null)
296: server.setAttribute(name, new Attribute("JNDIName",
297: jndiLocation));
298:
299: // This destination should be stopped when we are stopped
300: ArrayList depends = new ArrayList();
301: depends.add(serviceName);
302:
303: serviceController.create(name, depends);
304: serviceController.start(name);
305: }
306:
307: /**
308: * @jmx:managed-operation
309: */
310: public void destroyQueue(String name) throws Exception {
311: destroyDestination(getQueueObjectName(name));
312: }
313:
314: /**
315: * @jmx:managed-operation
316: */
317: public void destroyTopic(String name) throws Exception {
318: destroyDestination(getTopicObjectName(name));
319: }
320:
321: protected void destroyDestination(ObjectName name) throws Exception {
322: if (log.isDebugEnabled()) {
323: log.debug("Attempting to destroy destination: " + name);
324: }
325:
326: serviceController.stop(name);
327:
328: server.invoke(name, "removeAllMessages", new Object[] {},
329: new String[] {});
330: serviceController.destroy(name);
331: serviceController.remove(name);
332: }
333:
334: protected ObjectName getObjectName(MBeanServer server,
335: ObjectName name) throws MalformedObjectNameException {
336: // Save our object name to create destination names based on it
337: mqService = name;
338: return mqService;
339: }
340:
341: protected ObjectName getTopicObjectName(String name)
342: throws MalformedObjectNameException {
343: return new ObjectName(mqService.getDomain()
344: + ".destination:service=Topic,name=" + name);
345: }
346:
347: protected ObjectName getQueueObjectName(String name)
348: throws MalformedObjectNameException {
349: return new ObjectName(mqService.getDomain()
350: + ".destination:service=Queue,name=" + name);
351: }
352:
353: protected ServiceControllerMBean getServiceController() {
354: return serviceController;
355: }
356:
357: /**
358: * @see InterceptorMBean#getInterceptor()
359: */
360: public JMSServerInterceptor getInterceptor() {
361: return jmsServer;
362: }
363:
364: /**
365: * @see ServiceMBeanSupport#createService()
366: */
367: protected void createService() throws Exception {
368: super .createService();
369: jmsServer = new JMSDestinationManager(tempParameters);
370: }
371:
372: protected void startService() throws Exception {
373: // Get a proxy to the service controller
374: serviceController = (ServiceControllerMBean) MBeanProxyExt
375: .create(ServiceControllerMBean.class,
376: ServiceControllerMBean.OBJECT_NAME, server);
377:
378: PersistenceManager pm = (PersistenceManager) server
379: .getAttribute(persistenceManager, "Instance");
380: jmsServer.setPersistenceManager(pm);
381:
382: StateManager sm = (StateManager) server.getAttribute(
383: stateManager, "Instance");
384: jmsServer.setStateManager(sm);
385:
386: // We were either told the message cache or we get it from the
387: // persistence manager
388: MessageCache mc;
389: if (messageCache != null)
390: mc = (MessageCache) server.getAttribute(messageCache,
391: "Instance");
392: else
393: mc = pm.getMessageCacheInstance();
394: jmsServer.setMessageCache(mc);
395:
396: ThreadPool tp;
397: ThreadGroup tg;
398: if (threadPool == null) {
399: tg = new ThreadGroup("JBossMQ Server Threads");
400: tp = new BasicThreadPool("JMSThread", tg);
401: } else {
402: tp = (ThreadPool) server.getAttribute(threadPool,
403: "Instance");
404: if (tp instanceof BasicThreadPool)
405: tg = ((BasicThreadPool) tp).getThreadGroup();
406: else
407: tg = new ThreadGroup("JBossMQ Server Threads");
408: }
409: jmsServer.setThreadPool(tp);
410: jmsServer.setThreadGroup(tg);
411:
412: jmsServer.startServer();
413:
414: super .startService();
415: }
416:
417: protected void stopService() {
418: jmsServer.stopServer();
419: }
420:
421: protected void destroyService() throws Exception {
422: super .destroyService();
423: jmsServer = null;
424: }
425:
426: /**
427: * Sets the destination message counter history day limit
428: * <0: unlimited, =0: disabled, > 0 maximum day count
429: *
430: * @param days maximum day count
431: *
432: * @jmx:managed-attribute
433: */
434: public void setMessageCounterHistoryDayLimit(int days) {
435: if (days < -1)
436: days = -1;
437:
438: tempParameters.messageCounterHistoryDayLimit = days;
439:
440: }
441:
442: /**
443: * Gets the destination message counter history day limit
444: * @return Maximum day count
445: *
446: * @jmx:managed-attribute
447: */
448: public int getMessageCounterHistoryDayLimit() {
449: return tempParameters.messageCounterHistoryDayLimit;
450: }
451:
452: /**
453: * get message counter of all configured destinations
454: *
455: * @jmx:managed-operation
456: */
457: public MessageCounter[] getMessageCounter() throws Exception {
458: if (jmsServer == null)
459: return null;
460: return jmsServer.getMessageCounter();
461: }
462:
463: /**
464: * get message stats
465: *
466: * @jmx:managed-operation
467: */
468: public MessageStatistics[] getMessageStatistics() throws Exception {
469: if (jmsServer == null)
470: return null;
471: return MessageCounter.getMessageStatistics(jmsServer
472: .getMessageCounter());
473: }
474:
475: /**
476: * List message counter of all configured destinations as HTML table
477: *
478: * @jmx:managed-operation
479: */
480: public String listMessageCounter() throws Exception {
481: if (jmsServer == null)
482: return null;
483: MessageCounter[] counter = jmsServer.getMessageCounter();
484:
485: String ret = "<table width=\"100%\" border=\"1\" cellpadding=\"1\" cellspacing=\"1\">"
486: + "<tr>"
487: + "<th>Type</th>"
488: + "<th>Name</th>"
489: + "<th>Subscription</th>"
490: + "<th>Durable</th>"
491: + "<th>Count</th>"
492: + "<th>CountDelta</th>"
493: + "<th>Depth</th>"
494: + "<th>DepthDelta</th>"
495: + "<th>Last Add</th>" + "</tr>";
496:
497: String strNameLast = null;
498: String strTypeLast = null;
499: String strDestLast = null;
500:
501: String destData = "";
502: int destCount = 0;
503:
504: int countTotal = 0;
505: int countDeltaTotal = 0;
506: int depthTotal = 0;
507: int depthDeltaTotal = 0;
508:
509: int i = 0; // define outside of for statement, so variable
510: // still exists after for loop, because it is
511: // needed during output of last module data string
512:
513: for (i = 0; i < counter.length; i++) {
514: // get counter data
515: StringTokenizer tokens = new StringTokenizer(counter[i]
516: .getCounterAsString(), ",");
517:
518: String strType = tokens.nextToken();
519: String strName = tokens.nextToken();
520: String strSub = tokens.nextToken();
521: String strDurable = tokens.nextToken();
522:
523: String strDest = strType + "-" + strName;
524:
525: String strCount = tokens.nextToken();
526: String strCountDelta = tokens.nextToken();
527: String strDepth = tokens.nextToken();
528: String strDepthDelta = tokens.nextToken();
529: String strDate = tokens.nextToken();
530:
531: // update total count / depth values
532: countTotal += Integer.parseInt(strCount);
533: depthTotal += Integer.parseInt(strDepth);
534:
535: countDeltaTotal += Integer.parseInt(strCountDelta);
536: depthDeltaTotal += Integer.parseInt(strDepthDelta);
537:
538: if (strCountDelta.equalsIgnoreCase("0"))
539: strCountDelta = "-"; // looks better
540:
541: if (strDepthDelta.equalsIgnoreCase("0"))
542: strDepthDelta = "-"; // looks better
543:
544: // output destination counter data as HTML table row
545: // ( for topics with multiple subscriptions output
546: // type + name field as rowspans, looks better )
547: if (strDestLast != null && strDestLast.equals(strDest)) {
548: // still same destination -> append destination subscription data
549: destData += "<tr bgcolor=\"#"
550: + ((i % 2) == 0 ? "FFFFFF" : "F0F0F0") + "\">";
551: destCount += 1;
552: } else {
553: // start new destination data
554: if (strDestLast != null) {
555: // store last destination data string
556: ret += "<tr bgcolor=\"#"
557: + ((i % 2) == 0 ? "FFFFFF" : "F0F0F0")
558: + "\"><td rowspan=\"" + destCount + "\">"
559: + strTypeLast + "</td><td rowspan=\""
560: + destCount + "\">" + strNameLast + "</td>"
561: + destData;
562:
563: destData = "";
564: }
565:
566: destCount = 1;
567: }
568:
569: // counter data row
570: destData += "<td>" + strSub + "</td>" + "<td>" + strDurable
571: + "</td>" + "<td>" + strCount + "</td>" + "<td>"
572: + strCountDelta + "</td>" + "<td>" + strDepth
573: + "</td>" + "<td>" + strDepthDelta + "</td>"
574: + "<td>" + strDate + "</td>";
575:
576: // store current destination data for change detection
577: strTypeLast = strType;
578: strNameLast = strName;
579: strDestLast = strDest;
580: }
581:
582: if (strDestLast != null) {
583: // store last module data string
584: ret += "<tr bgcolor=\"#"
585: + ((i % 2) == 0 ? "FFFFFF" : "F0F0F0")
586: + "\"><td rowspan=\"" + destCount + "\">"
587: + strTypeLast + "</td><td rowspan=\"" + destCount
588: + "\">" + strNameLast + "</td>" + destData;
589: }
590:
591: // append summation info
592: ret += "<tr>"
593: + "<td><![CDATA[ ]]></td><td><![CDATA[ ]]></td>"
594: + "<td><![CDATA[ ]]></td><td><![CDATA[ ]]></td><td>"
595: + countTotal
596: + "</td><td>"
597: + (countDeltaTotal == 0 ? "-" : Integer
598: .toString(countDeltaTotal))
599: + "</td><td>"
600: + depthTotal
601: + "</td><td>"
602: + (depthDeltaTotal == 0 ? "-" : Integer
603: .toString(depthDeltaTotal))
604: + "</td><td>Total</td></tr></table>";
605:
606: return ret;
607: }
608:
609: /**
610: * Reset message counter of all configured destinations
611: *
612: * @jmx:managed-operation
613: */
614: public void resetMessageCounter() {
615: if (jmsServer == null)
616: return;
617: jmsServer.resetMessageCounter();
618: }
619:
620: public Map retrievePreparedTransactions() {
621: if (jmsServer == null)
622: return null;
623: Map map = jmsServer.getPersistenceManager().getTxManager()
624: .getPreparedTransactions();
625: HashMap result = new HashMap();
626: for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
627: Map.Entry entry = (Map.Entry) i.next();
628: Xid xid = (Xid) entry.getKey();
629: TxManager.PreparedInfo info = (TxManager.PreparedInfo) entry
630: .getValue();
631: if (xid != null && info != null)
632: result.put(xid, Boolean.valueOf(info.isInDoubt()));
633: }
634: return result;
635: }
636:
637: public String showPreparedTransactions() {
638: if (jmsServer == null)
639: return null;
640: Map map = jmsServer.getPersistenceManager().getTxManager()
641: .getPreparedTransactions();
642: JBossStringBuilder buffer = new JBossStringBuilder();
643: buffer
644: .append("<table width=\"100%\" border=\"1\" cellpadding=\"1\" cellspacing=\"1\">");
645: buffer
646: .append("<tr><th>Xid</th><th>In Doubt</th><th>Local TXIDs</th></tr>");
647: for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
648: Map.Entry entry = (Map.Entry) i.next();
649: Xid xid = (Xid) entry.getKey();
650: TxManager.PreparedInfo info = (TxManager.PreparedInfo) entry
651: .getValue();
652: if (xid != null && info != null) {
653: buffer.append("<tr><td>");
654: buffer.append(xid);
655: buffer.append("</td><td>");
656: buffer.append(info.isInDoubt());
657: buffer.append("</td><td>");
658: buffer.append(info.getTxids());
659: buffer.append("</td></tr>");
660: }
661: }
662: buffer.append("</table>");
663: return buffer.toString();
664: }
665: }
|