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 */
017package org.apache.activemq.broker.jmx;
018
019import java.io.IOException;
020import java.net.URISyntaxException;
021import java.util.ArrayList;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Map;
027import java.util.Map.Entry;
028
029import javax.jms.Connection;
030import javax.jms.InvalidSelectorException;
031import javax.jms.MessageProducer;
032import javax.jms.Session;
033import javax.management.MalformedObjectNameException;
034import javax.management.ObjectName;
035import javax.management.openmbean.CompositeData;
036import javax.management.openmbean.CompositeDataSupport;
037import javax.management.openmbean.CompositeType;
038import javax.management.openmbean.OpenDataException;
039import javax.management.openmbean.TabularData;
040import javax.management.openmbean.TabularDataSupport;
041import javax.management.openmbean.TabularType;
042
043import org.apache.activemq.ActiveMQConnectionFactory;
044import org.apache.activemq.broker.jmx.OpenTypeSupport.OpenTypeFactory;
045import org.apache.activemq.broker.region.Destination;
046import org.apache.activemq.broker.region.Subscription;
047import org.apache.activemq.broker.region.policy.AbortSlowConsumerStrategy;
048import org.apache.activemq.broker.region.policy.SlowConsumerStrategy;
049import org.apache.activemq.command.ActiveMQDestination;
050import org.apache.activemq.command.ActiveMQMessage;
051import org.apache.activemq.command.ActiveMQTextMessage;
052import org.apache.activemq.command.Message;
053import org.apache.activemq.filter.BooleanExpression;
054import org.apache.activemq.filter.NonCachedMessageEvaluationContext;
055import org.apache.activemq.selector.SelectorParser;
056import org.apache.activemq.store.MessageStore;
057import org.apache.activemq.util.URISupport;
058import org.slf4j.Logger;
059import org.slf4j.LoggerFactory;
060
061public class DestinationView implements DestinationViewMBean {
062    private static final Logger LOG = LoggerFactory.getLogger(DestinationViewMBean.class);
063    protected final Destination destination;
064    protected final ManagedRegionBroker broker;
065
066    public DestinationView(ManagedRegionBroker broker, Destination destination) {
067        this.broker = broker;
068        this.destination = destination;
069    }
070
071    public void gc() {
072        destination.gc();
073    }
074
075    @Override
076    public String getName() {
077        return destination.getName();
078    }
079
080    @Override
081    public void resetStatistics() {
082        destination.getDestinationStatistics().reset();
083    }
084
085    @Override
086    public long getEnqueueCount() {
087        return destination.getDestinationStatistics().getEnqueues().getCount();
088    }
089
090    @Override
091    public long getDequeueCount() {
092        return destination.getDestinationStatistics().getDequeues().getCount();
093    }
094
095    @Override
096    public long getForwardCount() {
097        return destination.getDestinationStatistics().getForwards().getCount();
098    }
099
100    @Override
101    public long getDispatchCount() {
102        return destination.getDestinationStatistics().getDispatched().getCount();
103    }
104
105    @Override
106    public long getDuplicateFromStoreCount() {
107        return destination.getDestinationStatistics().getDuplicateFromStore().getCount();
108    }
109
110    @Override
111    public long getInFlightCount() {
112        return destination.getDestinationStatistics().getInflight().getCount();
113    }
114
115    @Override
116    public long getExpiredCount() {
117        return destination.getDestinationStatistics().getExpired().getCount();
118    }
119
120    @Override
121    public long getConsumerCount() {
122        return destination.getDestinationStatistics().getConsumers().getCount();
123    }
124
125    @Override
126    public long getQueueSize() {
127        return destination.getDestinationStatistics().getMessages().getCount();
128    }
129
130    @Override
131    public long getStoreMessageSize() {
132        MessageStore messageStore = destination.getMessageStore();
133        return messageStore != null ? messageStore.getMessageStoreStatistics().getMessageSize().getTotalSize() : 0;
134    }
135
136    public long getMessagesCached() {
137        return destination.getDestinationStatistics().getMessagesCached().getCount();
138    }
139
140    @Override
141    public int getMemoryPercentUsage() {
142        return destination.getMemoryUsage().getPercentUsage();
143    }
144
145    @Override
146    public long getMemoryUsageByteCount() {
147        return destination.getMemoryUsage().getUsage();
148    }
149
150    @Override
151    public long getMemoryLimit() {
152        return destination.getMemoryUsage().getLimit();
153    }
154
155    @Override
156    public void setMemoryLimit(long limit) {
157        destination.getMemoryUsage().setLimit(limit);
158    }
159
160    @Override
161    public double getAverageEnqueueTime() {
162        return destination.getDestinationStatistics().getProcessTime().getAverageTime();
163    }
164
165    @Override
166    public int getTempUsagePercentUsage() {
167        return destination.getTempUsage().getPercentUsage();
168    }
169
170    @Override
171    public long getTempUsageLimit() {
172        return destination.getTempUsage().getLimit();
173    }
174
175    @Override
176    public void setTempUsageLimit(long limit) {
177        destination.getTempUsage().setLimit(limit);
178    }
179
180    @Override
181    public long getMaxEnqueueTime() {
182        return destination.getDestinationStatistics().getProcessTime().getMaxTime();
183    }
184
185    @Override
186    public long getMinEnqueueTime() {
187        return destination.getDestinationStatistics().getProcessTime().getMinTime();
188    }
189
190    /**
191     * @return the average size of a message (bytes)
192     */
193    @Override
194    public long getAverageMessageSize() {
195        // we are okay with the size without decimals so cast to long
196        return (long) destination.getDestinationStatistics().getMessageSize().getAverageSize();
197    }
198
199    /**
200     * @return the max size of a message (bytes)
201     */
202    @Override
203    public long getMaxMessageSize() {
204        return destination.getDestinationStatistics().getMessageSize().getMaxSize();
205    }
206
207    /**
208     * @return the min size of a message (bytes)
209     */
210    @Override
211    public long getMinMessageSize() {
212        return destination.getDestinationStatistics().getMessageSize().getMinSize();
213    }
214
215
216    @Override
217    public boolean isPrioritizedMessages() {
218        return destination.isPrioritizedMessages();
219    }
220
221    @Override
222    public CompositeData[] browse() throws OpenDataException {
223        try {
224            return browse(null);
225        } catch (InvalidSelectorException e) {
226            // should not happen.
227            throw new RuntimeException(e);
228        }
229    }
230
231    @Override
232    public CompositeData[] browse(String selector) throws OpenDataException, InvalidSelectorException {
233        Message[] messages = destination.browse();
234        ArrayList<CompositeData> c = new ArrayList<CompositeData>();
235
236        NonCachedMessageEvaluationContext ctx = new NonCachedMessageEvaluationContext();
237        ctx.setDestination(destination.getActiveMQDestination());
238        BooleanExpression selectorExpression = selector == null ? null : SelectorParser.parse(selector);
239
240        for (int i = 0; i < messages.length; i++) {
241            try {
242
243                if (selectorExpression == null) {
244                    c.add(OpenTypeSupport.convert(messages[i]));
245                } else {
246                    ctx.setMessageReference(messages[i]);
247                    if (selectorExpression.matches(ctx)) {
248                        c.add(OpenTypeSupport.convert(messages[i]));
249                    }
250                }
251
252            } catch (Throwable e) {
253                LOG.warn("exception browsing destination", e);
254            }
255        }
256
257        CompositeData rc[] = new CompositeData[c.size()];
258        c.toArray(rc);
259        return rc;
260    }
261
262    /**
263     * Browses the current destination returning a list of messages
264     */
265    @Override
266    public List<Object> browseMessages() throws InvalidSelectorException {
267        return browseMessages(null);
268    }
269
270    /**
271     * Browses the current destination with the given selector returning a list
272     * of messages
273     */
274    @Override
275    public List<Object> browseMessages(String selector) throws InvalidSelectorException {
276        Message[] messages = destination.browse();
277        ArrayList<Object> answer = new ArrayList<Object>();
278
279        NonCachedMessageEvaluationContext ctx = new NonCachedMessageEvaluationContext();
280        ctx.setDestination(destination.getActiveMQDestination());
281        BooleanExpression selectorExpression = selector == null ? null : SelectorParser.parse(selector);
282
283        for (int i = 0; i < messages.length; i++) {
284            try {
285                Message message = messages[i];
286                message.setReadOnlyBody(true);
287                if (selectorExpression == null) {
288                    answer.add(message);
289                } else {
290                    ctx.setMessageReference(message);
291                    if (selectorExpression.matches(ctx)) {
292                        answer.add(message);
293                    }
294                }
295
296            } catch (Throwable e) {
297                LOG.warn("exception browsing destination", e);
298            }
299        }
300        return answer;
301    }
302
303    @Override
304    public TabularData browseAsTable() throws OpenDataException {
305        try {
306            return browseAsTable(null);
307        } catch (InvalidSelectorException e) {
308            throw new RuntimeException(e);
309        }
310    }
311
312    @Override
313    public TabularData browseAsTable(String selector) throws OpenDataException, InvalidSelectorException {
314        OpenTypeFactory factory = OpenTypeSupport.getFactory(ActiveMQMessage.class);
315        Message[] messages = destination.browse();
316        CompositeType ct = factory.getCompositeType();
317        TabularType tt = new TabularType("MessageList", "MessageList", ct, new String[] { "JMSMessageID" });
318        TabularDataSupport rc = new TabularDataSupport(tt);
319
320        NonCachedMessageEvaluationContext ctx = new NonCachedMessageEvaluationContext();
321        ctx.setDestination(destination.getActiveMQDestination());
322        BooleanExpression selectorExpression = selector == null ? null : SelectorParser.parse(selector);
323
324        for (int i = 0; i < messages.length; i++) {
325            try {
326                if (selectorExpression == null) {
327                    rc.put(new CompositeDataSupport(ct, factory.getFields(messages[i])));
328                } else {
329                    ctx.setMessageReference(messages[i]);
330                    if (selectorExpression.matches(ctx)) {
331                        rc.put(new CompositeDataSupport(ct, factory.getFields(messages[i])));
332                    }
333                }
334            } catch (Throwable e) {
335                LOG.warn("exception browsing destination", e);
336            }
337        }
338
339        return rc;
340    }
341
342    @Override
343    public String sendTextMessageWithProperties(String properties) throws Exception {
344        Map<String, String> props = parseProps(properties, ",");
345        return sendTextMessage(props, props.remove("body"), props.remove("username"), props.remove("password"));
346    }
347
348    @Override
349    public String sendTextMessageWithProperties(String properties, String delimiter) throws Exception {
350        if (delimiter == null || delimiter.isEmpty()) {
351            return sendTextMessageWithProperties(properties);
352        } else {
353            Map<String, String> props = parseProps(properties, delimiter);
354            return sendTextMessage(props, props.remove("body"), props.remove("username"), props.remove("password"));
355        }
356    }
357
358    private Map<String, String> parseProps(String properties, String delimiter) {
359        String[] kvs = properties.split(delimiter);
360        Map<String, String> props = new HashMap<String, String>();
361        for (String kv : kvs) {
362            String[] it = kv.split("=");
363            if (it.length == 2) {
364                props.put(it[0],it[1]);
365            }
366        }
367        return props;
368    }
369
370    @Override
371    public String sendTextMessage(String body) throws Exception {
372        return sendTextMessage(Collections.EMPTY_MAP, body);
373    }
374
375    @Override
376    public String sendTextMessage(Map headers, String body) throws Exception {
377        return sendTextMessage(headers, body, null, null);
378    }
379
380    @Override
381    public String sendTextMessage(String body, String user, @Sensitive String password) throws Exception {
382        return sendTextMessage(Collections.EMPTY_MAP, body, user, password);
383    }
384
385    @Override
386    public String sendTextMessage(Map<String, String> headers, String body, String userName, @Sensitive String password) throws Exception {
387
388        String brokerUrl = "vm://" + broker.getBrokerName();
389        ActiveMQDestination dest = destination.getActiveMQDestination();
390
391        ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory(brokerUrl);
392        Connection connection = null;
393        try {
394            connection = cf.createConnection(userName, password);
395            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
396            MessageProducer producer = session.createProducer(dest);
397            ActiveMQTextMessage msg = (ActiveMQTextMessage) session.createTextMessage(body);
398
399            for (Iterator<Entry<String, String>> iter = headers.entrySet().iterator(); iter.hasNext();) {
400                Entry<String, String> entry = iter.next();
401                msg.setObjectProperty(entry.getKey(), entry.getValue());
402            }
403
404            producer.setDeliveryMode(msg.getJMSDeliveryMode());
405            producer.setPriority(msg.getPriority());
406            long ttl = 0;
407            if (msg.getExpiration() != 0) {
408                ttl = msg.getExpiration() - System.currentTimeMillis();
409            } else {
410                String timeToLive = headers.get("timeToLive");
411                if (timeToLive != null) {
412                    ttl = Integer.valueOf(timeToLive);
413                }
414            }
415            producer.setTimeToLive(ttl > 0 ? ttl : 0);
416            producer.send(msg);
417
418            return msg.getJMSMessageID();
419
420        } finally {
421            if (connection != null) {
422                connection.close();
423            }
424        }
425    }
426
427    @Override
428    public int getMaxAuditDepth() {
429        return destination.getMaxAuditDepth();
430    }
431
432    @Override
433    public int getMaxProducersToAudit() {
434        return destination.getMaxProducersToAudit();
435    }
436
437    public boolean isEnableAudit() {
438        return destination.isEnableAudit();
439    }
440
441    public void setEnableAudit(boolean enableAudit) {
442        destination.setEnableAudit(enableAudit);
443    }
444
445    @Override
446    public void setMaxAuditDepth(int maxAuditDepth) {
447        destination.setMaxAuditDepth(maxAuditDepth);
448    }
449
450    @Override
451    public void setMaxProducersToAudit(int maxProducersToAudit) {
452        destination.setMaxProducersToAudit(maxProducersToAudit);
453    }
454
455    @Override
456    public float getMemoryUsagePortion() {
457        return destination.getMemoryUsage().getUsagePortion();
458    }
459
460    @Override
461    public long getProducerCount() {
462        return destination.getDestinationStatistics().getProducers().getCount();
463    }
464
465    @Override
466    public boolean isProducerFlowControl() {
467        return destination.isProducerFlowControl();
468    }
469
470    @Override
471    public void setMemoryUsagePortion(float value) {
472        destination.getMemoryUsage().setUsagePortion(value);
473    }
474
475    @Override
476    public void setProducerFlowControl(boolean producerFlowControl) {
477        destination.setProducerFlowControl(producerFlowControl);
478    }
479
480    @Override
481    public boolean isAlwaysRetroactive() {
482        return destination.isAlwaysRetroactive();
483    }
484
485    @Override
486    public void setAlwaysRetroactive(boolean alwaysRetroactive) {
487        destination.setAlwaysRetroactive(alwaysRetroactive);
488    }
489
490    /**
491     * Set's the interval at which warnings about producers being blocked by
492     * resource usage will be triggered. Values of 0 or less will disable
493     * warnings
494     *
495     * @param blockedProducerWarningInterval the interval at which warning about
496     *            blocked producers will be triggered.
497     */
498    @Override
499    public void setBlockedProducerWarningInterval(long blockedProducerWarningInterval) {
500        destination.setBlockedProducerWarningInterval(blockedProducerWarningInterval);
501    }
502
503    /**
504     *
505     * @return the interval at which warning about blocked producers will be
506     *         triggered.
507     */
508    @Override
509    public long getBlockedProducerWarningInterval() {
510        return destination.getBlockedProducerWarningInterval();
511    }
512
513    @Override
514    public int getMaxPageSize() {
515        return destination.getMaxPageSize();
516    }
517
518    @Override
519    public void setMaxPageSize(int pageSize) {
520        destination.setMaxPageSize(pageSize);
521    }
522
523    @Override
524    public boolean isUseCache() {
525        return destination.isUseCache();
526    }
527
528    @Override
529    public void setUseCache(boolean value) {
530        destination.setUseCache(value);
531    }
532
533    @Override
534    public ObjectName[] getSubscriptions() throws IOException, MalformedObjectNameException {
535        List<Subscription> subscriptions = destination.getConsumers();
536        ObjectName[] answer = new ObjectName[subscriptions.size()];
537        ObjectName brokerObjectName = broker.getBrokerService().getBrokerObjectName();
538        int index = 0;
539        for (Subscription subscription : subscriptions) {
540            String connectionClientId = subscription.getContext().getClientId();
541            answer[index++] = BrokerMBeanSupport.createSubscriptionName(brokerObjectName, connectionClientId, subscription.getConsumerInfo());
542        }
543        return answer;
544    }
545
546    @Override
547    public ObjectName getSlowConsumerStrategy() throws IOException, MalformedObjectNameException {
548        ObjectName result = null;
549        SlowConsumerStrategy strategy = destination.getSlowConsumerStrategy();
550        if (strategy != null && strategy instanceof AbortSlowConsumerStrategy) {
551            result = broker.registerSlowConsumerStrategy((AbortSlowConsumerStrategy)strategy);
552        }
553        return result;
554    }
555
556    @Override
557    public String getOptions() {
558        Map<String, String> options = destination.getActiveMQDestination().getOptions();
559        String optionsString = "";
560        try {
561            if (options != null) {
562                optionsString = URISupport.createQueryString(options);
563            }
564        } catch (URISyntaxException ignored) {}
565        return optionsString;
566    }
567
568    @Override
569    public boolean isDLQ() {
570        return destination.getActiveMQDestination().isDLQ();
571    }
572
573    @Override
574    public void setDLQ(boolean val) {
575         destination.getActiveMQDestination().setDLQ(val);
576    }
577
578    @Override
579    public long getBlockedSends() {
580        return destination.getDestinationStatistics().getBlockedSends().getCount();
581    }
582
583    @Override
584    public double getAverageBlockedTime() {
585        return destination.getDestinationStatistics().getBlockedTime().getAverageTime();
586    }
587
588    @Override
589    public long getTotalBlockedTime() {
590        return destination.getDestinationStatistics().getBlockedTime().getTotalTime();
591    }
592
593    @Override
594    public boolean isSendDuplicateFromStoreToDLQ() {
595        return destination.isSendDuplicateFromStoreToDLQ();
596    }
597}