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.util.*;
021import java.util.Map.Entry;
022import java.util.concurrent.ConcurrentHashMap;
023import java.util.concurrent.CopyOnWriteArraySet;
024import java.util.concurrent.ExecutorService;
025import java.util.concurrent.ThreadPoolExecutor;
026
027import javax.jms.IllegalStateException;
028import javax.jms.JMSException;
029import javax.management.InstanceNotFoundException;
030import javax.management.MalformedObjectNameException;
031import javax.management.ObjectName;
032import javax.management.openmbean.CompositeData;
033import javax.management.openmbean.CompositeDataSupport;
034import javax.management.openmbean.CompositeType;
035import javax.management.openmbean.OpenDataException;
036import javax.management.openmbean.TabularData;
037import javax.management.openmbean.TabularDataSupport;
038import javax.management.openmbean.TabularType;
039
040import org.apache.activemq.broker.Broker;
041import org.apache.activemq.broker.BrokerService;
042import org.apache.activemq.broker.ConnectionContext;
043import org.apache.activemq.broker.ProducerBrokerExchange;
044import org.apache.activemq.broker.jmx.OpenTypeSupport.OpenTypeFactory;
045import org.apache.activemq.broker.region.AbstractRegion;
046import org.apache.activemq.broker.region.Destination;
047import org.apache.activemq.broker.region.DestinationFactory;
048import org.apache.activemq.broker.region.DestinationInterceptor;
049import org.apache.activemq.broker.region.DurableTopicSubscription;
050import org.apache.activemq.broker.region.MessageReference;
051import org.apache.activemq.broker.region.NullMessageReference;
052import org.apache.activemq.broker.region.Queue;
053import org.apache.activemq.broker.region.Region;
054import org.apache.activemq.broker.region.RegionBroker;
055import org.apache.activemq.broker.region.Subscription;
056import org.apache.activemq.broker.region.Topic;
057import org.apache.activemq.broker.region.TopicRegion;
058import org.apache.activemq.broker.region.TopicSubscription;
059import org.apache.activemq.broker.region.policy.AbortSlowAckConsumerStrategy;
060import org.apache.activemq.broker.region.policy.AbortSlowConsumerStrategy;
061import org.apache.activemq.command.ActiveMQDestination;
062import org.apache.activemq.command.ActiveMQMessage;
063import org.apache.activemq.command.ActiveMQTopic;
064import org.apache.activemq.command.ConnectionInfo;
065import org.apache.activemq.command.ConsumerId;
066import org.apache.activemq.command.ConsumerInfo;
067import org.apache.activemq.command.Message;
068import org.apache.activemq.command.MessageAck;
069import org.apache.activemq.command.MessageId;
070import org.apache.activemq.command.ProducerInfo;
071import org.apache.activemq.command.SubscriptionInfo;
072import org.apache.activemq.thread.Scheduler;
073import org.apache.activemq.thread.TaskRunnerFactory;
074import org.apache.activemq.transaction.XATransaction;
075import org.apache.activemq.usage.SystemUsage;
076import org.apache.activemq.util.ServiceStopper;
077import org.apache.activemq.util.SubscriptionKey;
078import org.slf4j.Logger;
079import org.slf4j.LoggerFactory;
080
081public class ManagedRegionBroker extends RegionBroker {
082    private static final Logger LOG = LoggerFactory.getLogger(ManagedRegionBroker.class);
083    private final ManagementContext managementContext;
084    private final ObjectName brokerObjectName;
085    private final Map<ObjectName, DestinationView> topics = new ConcurrentHashMap<>();
086    private final Map<ObjectName, DestinationView> queues = new ConcurrentHashMap<>();
087    private final Map<ObjectName, DestinationView> temporaryQueues = new ConcurrentHashMap<>();
088    private final Map<ObjectName, DestinationView> temporaryTopics = new ConcurrentHashMap<>();
089    private final Map<ObjectName, SubscriptionView> queueSubscribers = new ConcurrentHashMap<>();
090    private final Map<ObjectName, SubscriptionView> topicSubscribers = new ConcurrentHashMap<>();
091    private final Map<ObjectName, SubscriptionView> durableTopicSubscribers = new ConcurrentHashMap<>();
092    private final Map<ObjectName, SubscriptionView> inactiveDurableTopicSubscribers = new ConcurrentHashMap<>();
093    private final Map<ObjectName, SubscriptionView> temporaryQueueSubscribers = new ConcurrentHashMap<>();
094    private final Map<ObjectName, SubscriptionView> temporaryTopicSubscribers = new ConcurrentHashMap<>();
095    private final Map<ObjectName, ProducerView> queueProducers = new ConcurrentHashMap<>();
096    private final Map<ObjectName, ProducerView> topicProducers = new ConcurrentHashMap<>();
097    private final Map<ObjectName, ProducerView> temporaryQueueProducers = new ConcurrentHashMap<>();
098    private final Map<ObjectName, ProducerView> temporaryTopicProducers = new ConcurrentHashMap<>();
099    private final Map<ObjectName, ProducerView> dynamicDestinationProducers = new ConcurrentHashMap<>();
100    private final Map<SubscriptionKey, ObjectName> subscriptionKeys = new ConcurrentHashMap<>();
101    private final Map<Subscription, ObjectName> subscriptionMap = new ConcurrentHashMap<>();
102    private final Set<ObjectName> registeredMBeans = ConcurrentHashMap.newKeySet();
103    /* This is the first broker in the broker interceptor chain. */
104    private Broker contextBroker;
105
106    private final ExecutorService asyncInvokeService;
107    private final long mbeanTimeout;
108
109    public ManagedRegionBroker(BrokerService brokerService, ManagementContext context, ObjectName brokerObjectName, TaskRunnerFactory taskRunnerFactory, SystemUsage memoryManager,
110                               DestinationFactory destinationFactory, DestinationInterceptor destinationInterceptor,Scheduler scheduler,ThreadPoolExecutor executor) throws IOException {
111        super(brokerService, taskRunnerFactory, memoryManager, destinationFactory, destinationInterceptor,scheduler,executor);
112        this.managementContext = context;
113        this.brokerObjectName = brokerObjectName;
114        this.mbeanTimeout = brokerService.getMbeanInvocationTimeout();
115        this.asyncInvokeService = mbeanTimeout > 0 ? executor : null;;
116    }
117
118    @Override
119    public void start() throws Exception {
120        super.start();
121        // build all existing durable subscriptions
122        buildExistingSubscriptions();
123    }
124
125    @Override
126    protected void doStop(ServiceStopper stopper) {
127        super.doStop(stopper);
128        // lets remove any mbeans not yet removed
129        for (Iterator<ObjectName> iter = registeredMBeans.iterator(); iter.hasNext();) {
130            ObjectName name = iter.next();
131            try {
132                managementContext.unregisterMBean(name);
133            } catch (InstanceNotFoundException e) {
134                LOG.warn("The MBean {} is no longer registered with JMX", name);
135            } catch (Exception e) {
136                stopper.onException(this, e);
137            }
138        }
139        registeredMBeans.clear();
140    }
141
142    @Override
143    protected Region createQueueRegion(SystemUsage memoryManager, TaskRunnerFactory taskRunnerFactory, DestinationFactory destinationFactory) {
144        return new ManagedQueueRegion(this, destinationStatistics, memoryManager, taskRunnerFactory, destinationFactory);
145    }
146
147    @Override
148    protected Region createTempQueueRegion(SystemUsage memoryManager, TaskRunnerFactory taskRunnerFactory, DestinationFactory destinationFactory) {
149        return new ManagedTempQueueRegion(this, destinationStatistics, memoryManager, taskRunnerFactory, destinationFactory);
150    }
151
152    @Override
153    protected Region createTempTopicRegion(SystemUsage memoryManager, TaskRunnerFactory taskRunnerFactory, DestinationFactory destinationFactory) {
154        return new ManagedTempTopicRegion(this, destinationStatistics, memoryManager, taskRunnerFactory, destinationFactory);
155    }
156
157    @Override
158    protected Region createTopicRegion(SystemUsage memoryManager, TaskRunnerFactory taskRunnerFactory, DestinationFactory destinationFactory) {
159        return new ManagedTopicRegion(this, destinationStatistics, memoryManager, taskRunnerFactory, destinationFactory);
160    }
161
162    public void register(ActiveMQDestination destName, Destination destination) {
163        // TODO refactor to allow views for custom destinations
164        try {
165            ObjectName objectName = BrokerMBeanSupport.createDestinationName(brokerObjectName, destName);
166            DestinationView view;
167            if (destination instanceof Queue) {
168                view = new QueueView(this, (Queue)destination);
169            } else if (destination instanceof Topic) {
170                view = new TopicView(this, (Topic)destination);
171            } else {
172                view = null;
173                LOG.warn("JMX View is not supported for custom destination {}", destination);
174            }
175            if (view != null) {
176                registerDestination(objectName, destName, view);
177            }
178        } catch (Exception e) {
179            LOG.error("Failed to register destination {}", destName, e);
180        }
181    }
182
183    public void unregister(ActiveMQDestination destName) {
184        try {
185            ObjectName objectName = BrokerMBeanSupport.createDestinationName(brokerObjectName, destName);
186            unregisterDestination(objectName);
187        } catch (Exception e) {
188            LOG.error("Failed to unregister {}", destName, e);
189        }
190    }
191
192    public ObjectName registerSubscription(ConnectionContext context, Subscription sub) {
193        String connectionClientId = context.getClientId();
194
195        SubscriptionKey key = new SubscriptionKey(context.getClientId(), sub.getConsumerInfo().getSubscriptionName());
196        try {
197            ObjectName objectName = BrokerMBeanSupport.createSubscriptionName(brokerObjectName, connectionClientId, sub.getConsumerInfo());
198            SubscriptionView view;
199            if (sub.getConsumerInfo().getConsumerId().getConnectionId().equals("OFFLINE")) {
200                // add offline subscribers to inactive list
201                SubscriptionInfo info = new SubscriptionInfo();
202                info.setClientId(context.getClientId());
203                info.setSubscriptionName(sub.getConsumerInfo().getSubscriptionName());
204                info.setDestination(sub.getConsumerInfo().getDestination());
205                info.setSelector(sub.getSelector());
206                addInactiveSubscription(key, info, sub);
207            } else {
208                String userName = brokerService.isPopulateUserNameInMBeans() ? context.getUserName() : null;
209                if (sub.getConsumerInfo().isDurable()) {
210                    view = new DurableSubscriptionView(this, brokerService, context.getClientId(), userName, sub);
211                } else {
212                    if (sub instanceof TopicSubscription) {
213                        view = new TopicSubscriptionView(context.getClientId(), userName, (TopicSubscription) sub);
214                    } else {
215                        view = new SubscriptionView(context.getClientId(), userName, sub);
216                    }
217                }
218                registerSubscription(objectName, sub.getConsumerInfo(), key, view);
219            }
220            subscriptionMap.put(sub, objectName);
221            return objectName;
222        } catch (Exception e) {
223            LOG.error("Failed to register subscription {}", sub, e);
224            return null;
225        }
226    }
227
228    @Override
229    public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
230        super.addConnection(context, info);
231        this.contextBroker.getBrokerService().incrementCurrentConnections();
232        this.contextBroker.getBrokerService().incrementTotalConnections();
233    }
234
235    @Override
236    public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Exception {
237        super.removeConnection(context, info, error);
238        this.contextBroker.getBrokerService().decrementCurrentConnections();
239    }
240
241    @Override
242    public Subscription addConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
243        Subscription sub = super.addConsumer(context, info);
244        SubscriptionKey subscriptionKey = new SubscriptionKey(sub.getContext().getClientId(), sub.getConsumerInfo().getSubscriptionName());
245        ObjectName inactiveName = subscriptionKeys.get(subscriptionKey);
246        if (inactiveName != null) {
247            // if it was inactive, register it
248            registerSubscription(context, sub);
249        }
250        return sub;
251    }
252
253    @Override
254    public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
255        //Find subscriptions quickly by relying on the maps contained in the different Regions
256        //that map consumer ids and subscriptions
257        final Set<Subscription> subscriptions = findSubscriptions(info);
258
259        if (!subscriptions.isEmpty()) {
260            for (Subscription sub : subscriptions) {
261                // unregister all consumer subs
262                unregisterSubscription(subscriptionMap.get(sub), true);
263                break;
264            }
265        } else {
266            //Fall back to old slow approach where we go through the entire subscription map case something went wrong
267            //and no subscriptions were found - should generally not happen
268            for (Subscription sub : subscriptionMap.keySet()) {
269                if (sub.getConsumerInfo().equals(info)) {
270                    unregisterSubscription(subscriptionMap.get(sub), true);
271                }
272            }
273        }
274
275        super.removeConsumer(context, info);
276    }
277
278    private Set<Subscription> findSubscriptions(final ConsumerInfo info) {
279        final Set<Subscription> subscriptions = new HashSet<>();
280
281        try {
282            if (info.getDestination() != null) {
283                final ActiveMQDestination consumerDest = info.getDestination();
284                //If it's composite then go through and find the subscription for every dest in case different
285                if (consumerDest.isComposite()) {
286                    ActiveMQDestination[] destinations = consumerDest.getCompositeDestinations();
287                    for (ActiveMQDestination destination : destinations) {
288                        addSubscriptionToList(subscriptions, info.getConsumerId(), destination);
289                    }
290                } else {
291                    //This is the case for a non-composite destination which would be most of the time
292                    addSubscriptionToList(subscriptions, info.getConsumerId(), info.getDestination());
293                }
294            }
295        } catch (Exception e) {
296            LOG.warn("Error finding subscription {}: {}", info, e.getMessage());
297        }
298
299        return subscriptions;
300    }
301
302    private void addSubscriptionToList(Set<Subscription> subscriptions,
303        ConsumerId consumerId, ActiveMQDestination dest) throws JMSException {
304        final Subscription matchingSub = ((AbstractRegion) this.getRegion(dest))
305            .getSubscriptions().get(consumerId);
306        if (matchingSub != null) {
307            subscriptions.add(matchingSub);
308        }
309    }
310
311    @Override
312    public void addProducer(ConnectionContext context, ProducerInfo info) throws Exception {
313        super.addProducer(context, info);
314        String connectionClientId = context.getClientId();
315        ObjectName objectName = BrokerMBeanSupport.createProducerName(brokerObjectName, context.getClientId(), info);
316        String userName = brokerService.isPopulateUserNameInMBeans() ? context.getUserName() : null;
317        ProducerView view = new ProducerView(info, connectionClientId, userName, this);
318        registerProducer(objectName, info.getDestination(), view);
319    }
320
321    @Override
322    public void removeProducer(ConnectionContext context, ProducerInfo info) throws Exception {
323        ObjectName objectName = BrokerMBeanSupport.createProducerName(brokerObjectName, context.getClientId(), info);
324        unregisterProducer(objectName);
325        super.removeProducer(context, info);
326    }
327
328    @Override
329    public void send(ProducerBrokerExchange exchange, Message message) throws Exception {
330        if (exchange != null && exchange.getProducerState() != null && exchange.getProducerState().getInfo() != null) {
331            ProducerInfo info = exchange.getProducerState().getInfo();
332            if (info.getDestination() == null && info.getProducerId() != null) {
333                ObjectName objectName = BrokerMBeanSupport.createProducerName(brokerObjectName, exchange.getConnectionContext().getClientId(), info);
334                ProducerView view = this.dynamicDestinationProducers.get(objectName);
335                if (view != null) {
336                    ActiveMQDestination dest = message.getDestination();
337                    if (dest != null) {
338                        view.setLastUsedDestinationName(dest);
339                    }
340                }
341            }
342         }
343        super.send(exchange, message);
344    }
345
346    public void unregisterSubscription(Subscription sub) {
347        ObjectName name = subscriptionMap.remove(sub);
348        if (name != null) {
349            try {
350                SubscriptionKey subscriptionKey = new SubscriptionKey(sub.getContext().getClientId(), sub.getConsumerInfo().getSubscriptionName());
351                ObjectName inactiveName = subscriptionKeys.remove(subscriptionKey);
352                if (inactiveName != null) {
353                    inactiveDurableTopicSubscribers.remove(inactiveName);
354                    managementContext.unregisterMBean(inactiveName);
355                }
356            } catch (Exception e) {
357                LOG.error("Failed to unregister subscription {}", sub, e);
358            }
359        }
360    }
361
362    protected void registerDestination(ObjectName key, ActiveMQDestination dest, DestinationView view) throws Exception {
363        if (dest.isQueue()) {
364            if (dest.isTemporary()) {
365                temporaryQueues.put(key, view);
366            } else {
367                queues.put(key, view);
368            }
369        } else {
370            if (dest.isTemporary()) {
371                temporaryTopics.put(key, view);
372            } else {
373                topics.put(key, view);
374            }
375        }
376        try {
377            if (AsyncAnnotatedMBean.registerMBean(asyncInvokeService, mbeanTimeout, managementContext, view, key) != null) {
378                registeredMBeans.add(key);
379            }
380        } catch (Throwable e) {
381            LOG.warn("Failed to register MBean {}", key);
382            LOG.debug("Failure reason: ", e);
383        }
384    }
385
386    protected void unregisterDestination(ObjectName key) throws Exception {
387
388        DestinationView view = removeAndRemember(topics, key, null);
389        view = removeAndRemember(queues, key, view);
390        view = removeAndRemember(temporaryQueues, key, view);
391        view = removeAndRemember(temporaryTopics, key, view);
392        if (registeredMBeans.remove(key)) {
393            try {
394                managementContext.unregisterMBean(key);
395            } catch (Throwable e) {
396                LOG.warn("Failed to unregister MBean {}", key);
397                LOG.debug("Failure reason: ", e);
398            }
399        }
400        if (view != null) {
401            key = view.getSlowConsumerStrategy();
402            if (key!= null && registeredMBeans.remove(key)) {
403                try {
404                    managementContext.unregisterMBean(key);
405                } catch (Throwable e) {
406                    LOG.warn("Failed to unregister slow consumer strategy MBean {}", key);
407                    LOG.debug("Failure reason: ", e);
408                }
409            }
410        }
411    }
412
413    protected void registerProducer(ObjectName key, ActiveMQDestination dest, ProducerView view) throws Exception {
414
415        if (dest != null) {
416            if (dest.isQueue()) {
417                if (dest.isTemporary()) {
418                    temporaryQueueProducers.put(key, view);
419                } else {
420                    queueProducers.put(key, view);
421                }
422            } else {
423                if (dest.isTemporary()) {
424                    temporaryTopicProducers.put(key, view);
425                } else {
426                    topicProducers.put(key, view);
427                }
428            }
429        } else {
430            dynamicDestinationProducers.put(key, view);
431        }
432
433        try {
434            if (AsyncAnnotatedMBean.registerMBean(asyncInvokeService, mbeanTimeout, managementContext, view, key) != null) {
435                registeredMBeans.add(key);
436            }
437        } catch (Throwable e) {
438            LOG.warn("Failed to register MBean {}", key);
439            LOG.debug("Failure reason: ", e);
440        }
441    }
442
443    protected void unregisterProducer(ObjectName key) throws Exception {
444        queueProducers.remove(key);
445        topicProducers.remove(key);
446        temporaryQueueProducers.remove(key);
447        temporaryTopicProducers.remove(key);
448        dynamicDestinationProducers.remove(key);
449        if (registeredMBeans.remove(key)) {
450            try {
451                managementContext.unregisterMBean(key);
452            } catch (Throwable e) {
453                LOG.warn("Failed to unregister MBean {}", key);
454                LOG.debug("Failure reason: ", e);
455            }
456        }
457    }
458
459    private DestinationView removeAndRemember(Map<ObjectName, DestinationView> map, ObjectName key, DestinationView view) {
460        DestinationView candidate = map.remove(key);
461        if (candidate != null && view == null) {
462            view = candidate;
463        }
464        return candidate != null ? candidate : view;
465    }
466
467    protected void registerSubscription(ObjectName key, ConsumerInfo info, SubscriptionKey subscriptionKey, SubscriptionView view) throws Exception {
468        ActiveMQDestination dest = info.getDestination();
469        if (dest.isQueue()) {
470            if (dest.isTemporary()) {
471                temporaryQueueSubscribers.put(key, view);
472            } else {
473                queueSubscribers.put(key, view);
474            }
475        } else {
476            if (dest.isTemporary()) {
477                temporaryTopicSubscribers.put(key, view);
478            } else {
479                if (info.isDurable()) {
480                    durableTopicSubscribers.put(key, view);
481                    // unregister any inactive durable subs
482                    try {
483                        ObjectName inactiveName = subscriptionKeys.get(subscriptionKey);
484                        if (inactiveName != null) {
485                            inactiveDurableTopicSubscribers.remove(inactiveName);
486                            registeredMBeans.remove(inactiveName);
487                            managementContext.unregisterMBean(inactiveName);
488                        }
489                    } catch (Throwable e) {
490                        LOG.error("Unable to unregister inactive durable subscriber {}", subscriptionKey, e);
491                    }
492                } else {
493                    topicSubscribers.put(key, view);
494                }
495            }
496        }
497
498        try {
499            if (AsyncAnnotatedMBean.registerMBean(asyncInvokeService, mbeanTimeout, managementContext, view, key) != null) {
500                registeredMBeans.add(key);
501            }
502        } catch (Throwable e) {
503            LOG.warn("Failed to register MBean {}", key);
504            LOG.debug("Failure reason: ", e);
505        }
506    }
507
508    protected void unregisterSubscription(ObjectName key, boolean addToInactive) throws Exception {
509        queueSubscribers.remove(key);
510        topicSubscribers.remove(key);
511        temporaryQueueSubscribers.remove(key);
512        temporaryTopicSubscribers.remove(key);
513        if (registeredMBeans.remove(key)) {
514            try {
515                managementContext.unregisterMBean(key);
516            } catch (Throwable e) {
517                LOG.warn("Failed to unregister MBean {}", key);
518                LOG.debug("Failure reason: ", e);
519            }
520        }
521        DurableSubscriptionView view = (DurableSubscriptionView)durableTopicSubscribers.remove(key);
522        if (view != null) {
523            // need to put this back in the inactive list
524            SubscriptionKey subscriptionKey = new SubscriptionKey(view.getClientId(), view.getSubscriptionName());
525            if (addToInactive) {
526                SubscriptionInfo info = new SubscriptionInfo();
527                info.setClientId(subscriptionKey.getClientId());
528                info.setSubscriptionName(subscriptionKey.getSubscriptionName());
529                info.setDestination(new ActiveMQTopic(view.getDestinationName()));
530                info.setSelector(view.getSelector());
531                addInactiveSubscription(subscriptionKey, info, (brokerService.isKeepDurableSubsActive() ? view.subscription : null));
532            }
533        }
534    }
535
536    protected void buildExistingSubscriptions() throws Exception {
537        Map<SubscriptionKey, SubscriptionInfo> subscriptions = new HashMap<>();
538        Set<ActiveMQDestination> destinations = destinationFactory.getDestinations();
539        if (destinations != null) {
540            for (ActiveMQDestination dest : destinations) {
541                if (dest.isTopic()) {
542                    SubscriptionInfo[] infos = destinationFactory.getAllDurableSubscriptions((ActiveMQTopic)dest);
543                    if (infos != null) {
544                        for (int i = 0; i < infos.length; i++) {
545                            SubscriptionInfo info = infos[i];
546                            SubscriptionKey key = new SubscriptionKey(info);
547                            if (!alreadyKnown(key)) {
548                                LOG.debug("Restoring durable subscription MBean {}", info);
549                                subscriptions.put(key, info);
550                            }
551                        }
552                    }
553                }
554            }
555        }
556
557        for (Map.Entry<SubscriptionKey, SubscriptionInfo> entry : subscriptions.entrySet()) {
558            addInactiveSubscription(entry.getKey(), entry.getValue(), null);
559        }
560    }
561
562    private boolean alreadyKnown(SubscriptionKey key) {
563        boolean known = false;
564        known = ((TopicRegion) getTopicRegion()).durableSubscriptionExists(key);
565        LOG.trace("Sub with key: {}, {} already registered", key, (known ? "": "not"));
566        return known;
567    }
568
569    protected void addInactiveSubscription(SubscriptionKey key, SubscriptionInfo info, Subscription subscription) {
570        try {
571            ConsumerInfo offlineConsumerInfo = subscription != null ? subscription.getConsumerInfo() : ((TopicRegion)getTopicRegion()).createInactiveConsumerInfo(info);
572            ObjectName objectName = BrokerMBeanSupport.createSubscriptionName(brokerObjectName, info.getClientId(), offlineConsumerInfo);
573            SubscriptionView view = new InactiveDurableSubscriptionView(this, brokerService, key.getClientId(), info, subscription);
574
575            try {
576                if (AsyncAnnotatedMBean.registerMBean(asyncInvokeService, mbeanTimeout, managementContext, view, objectName) != null) {
577                    registeredMBeans.add(objectName);
578                }
579            } catch (Throwable e) {
580                LOG.warn("Failed to register MBean {}", key);
581                LOG.debug("Failure reason: ", e);
582            }
583
584            inactiveDurableTopicSubscribers.put(objectName, view);
585            subscriptionKeys.put(key, objectName);
586        } catch (Exception e) {
587            LOG.error("Failed to register subscription {}", info, e);
588        }
589    }
590
591    public CompositeData[] browse(SubscriptionView view) throws OpenDataException {
592        Message[] messages = getSubscriberMessages(view);
593        CompositeData c[] = new CompositeData[messages.length];
594        for (int i = 0; i < c.length; i++) {
595            try {
596                c[i] = OpenTypeSupport.convert(messages[i]);
597            } catch (Throwable e) {
598                LOG.error("Failed to browse: {}", view, e);
599            }
600        }
601        return c;
602    }
603
604    public TabularData browseAsTable(SubscriptionView view) throws OpenDataException {
605        OpenTypeFactory factory = OpenTypeSupport.getFactory(ActiveMQMessage.class);
606        Message[] messages = getSubscriberMessages(view);
607        CompositeType ct = factory.getCompositeType();
608        TabularType tt = new TabularType("MessageList", "MessageList", ct, new String[] {"JMSMessageID"});
609        TabularDataSupport rc = new TabularDataSupport(tt);
610        for (int i = 0; i < messages.length; i++) {
611            rc.put(new CompositeDataSupport(ct, factory.getFields(messages[i])));
612        }
613        return rc;
614    }
615
616    public void remove(SubscriptionView view, String messageId)  throws Exception {
617        ActiveMQDestination destination = getTopicDestination(view);
618        if (destination != null) {
619            final Destination topic = getTopicRegion().getDestinationMap().get(destination);
620            final MessageAck messageAck = new MessageAck();
621            messageAck.setMessageID(new MessageId(messageId));
622            messageAck.setDestination(destination);
623
624            topic.getMessageStore().removeMessage(brokerService.getAdminConnectionContext(), messageAck);
625
626            // if sub is active, remove from cursor
627            if (view.subscription instanceof DurableTopicSubscription) {
628                final DurableTopicSubscription durableTopicSubscription = (DurableTopicSubscription) view.subscription;
629                final MessageReference messageReference = new NullMessageReference();
630                messageReference.getMessage().setMessageId(messageAck.getFirstMessageId());
631                durableTopicSubscription.getPending().remove(messageReference);
632            }
633
634        } else {
635            throw new IllegalStateException("can't determine topic for sub:" + view);
636        }
637    }
638
639    protected Message[] getSubscriberMessages(SubscriptionView view) {
640        ActiveMQDestination destination = getTopicDestination(view);
641        if (destination != null) {
642            Destination topic = getTopicRegion().getDestinationMap().get(destination);
643            return topic.browse();
644
645        } else {
646            LOG.warn("can't determine topic to browse for sub:" + view);
647            return new Message[]{};
648        }
649    }
650
651    private ActiveMQDestination getTopicDestination(SubscriptionView view) {
652        ActiveMQDestination destination = null;
653        if (view.subscription instanceof DurableTopicSubscription) {
654            destination = new ActiveMQTopic(view.getDestinationName());
655        } else if (view instanceof InactiveDurableSubscriptionView) {
656            destination = ((InactiveDurableSubscriptionView)view).subscriptionInfo.getDestination();
657        }
658        return destination;
659    }
660
661    private ObjectName[] onlyNonSuppressed (Set<ObjectName> set){
662        List<ObjectName> nonSuppressed = new ArrayList<>();
663        for(ObjectName key : set){
664            if (managementContext.isAllowedToRegister(key)){
665                nonSuppressed.add(key);
666            }
667        }
668        return nonSuppressed.toArray(new ObjectName[nonSuppressed.size()]);
669    }
670
671    protected ObjectName[] getTopics() {
672        Set<ObjectName> set = topics.keySet();
673        return set.toArray(new ObjectName[set.size()]);
674    }
675
676    protected ObjectName[] getTopicsNonSuppressed() {
677        return onlyNonSuppressed(topics.keySet());
678    }
679
680    protected ObjectName[] getQueues() {
681        Set<ObjectName> set = queues.keySet();
682        return set.toArray(new ObjectName[set.size()]);
683    }
684
685    protected ObjectName[] getQueuesNonSuppressed() {
686        return onlyNonSuppressed(queues.keySet());
687    }
688
689    protected ObjectName[] getTemporaryTopics() {
690        Set<ObjectName> set = temporaryTopics.keySet();
691        return set.toArray(new ObjectName[set.size()]);
692    }
693
694    protected ObjectName[] getTemporaryTopicsNonSuppressed() {
695        return onlyNonSuppressed(temporaryTopics.keySet());
696    }
697
698    protected ObjectName[] getTemporaryQueues() {
699        Set<ObjectName> set = temporaryQueues.keySet();
700        return set.toArray(new ObjectName[set.size()]);
701    }
702
703    protected ObjectName[] getTemporaryQueuesNonSuppressed() {
704        return onlyNonSuppressed(temporaryQueues.keySet());
705    }
706
707    protected ObjectName[] getTopicSubscribers() {
708        Set<ObjectName> set = topicSubscribers.keySet();
709        return set.toArray(new ObjectName[set.size()]);
710    }
711
712    protected ObjectName[] getTopicSubscribersNonSuppressed() {
713        return onlyNonSuppressed(topicSubscribers.keySet());
714    }
715
716    protected ObjectName[] getDurableTopicSubscribers() {
717        Set<ObjectName> set = durableTopicSubscribers.keySet();
718        return set.toArray(new ObjectName[set.size()]);
719    }
720
721    protected ObjectName[] getDurableTopicSubscribersNonSuppressed() {
722        return onlyNonSuppressed(durableTopicSubscribers.keySet());
723    }
724
725    protected ObjectName[] getQueueSubscribers() {
726        Set<ObjectName> set = queueSubscribers.keySet();
727        return set.toArray(new ObjectName[set.size()]);
728    }
729
730    protected ObjectName[] getQueueSubscribersNonSuppressed() {
731        return onlyNonSuppressed(queueSubscribers.keySet());
732    }
733
734    protected ObjectName[] getTemporaryTopicSubscribers() {
735        Set<ObjectName> set = temporaryTopicSubscribers.keySet();
736        return set.toArray(new ObjectName[set.size()]);
737    }
738
739    protected ObjectName[] getTemporaryTopicSubscribersNonSuppressed() {
740        return onlyNonSuppressed(temporaryTopicSubscribers.keySet());
741    }
742
743    protected ObjectName[] getTemporaryQueueSubscribers() {
744        Set<ObjectName> set = temporaryQueueSubscribers.keySet();
745        return set.toArray(new ObjectName[set.size()]);
746    }
747
748    protected ObjectName[] getTemporaryQueueSubscribersNonSuppressed() {
749        return onlyNonSuppressed(temporaryQueueSubscribers.keySet());
750    }
751
752    protected ObjectName[] getInactiveDurableTopicSubscribers() {
753        Set<ObjectName> set = inactiveDurableTopicSubscribers.keySet();
754        return set.toArray(new ObjectName[set.size()]);
755    }
756
757    protected ObjectName[] getInactiveDurableTopicSubscribersNonSuppressed() {
758        return onlyNonSuppressed(inactiveDurableTopicSubscribers.keySet());
759    }
760
761    protected ObjectName[] getTopicProducers() {
762        Set<ObjectName> set = topicProducers.keySet();
763        return set.toArray(new ObjectName[set.size()]);
764    }
765
766    protected ObjectName[] getTopicProducersNonSuppressed() {
767        return onlyNonSuppressed(topicProducers.keySet());
768    }
769
770    protected ObjectName[] getQueueProducers() {
771        Set<ObjectName> set = queueProducers.keySet();
772        return set.toArray(new ObjectName[set.size()]);
773    }
774
775    protected ObjectName[] getQueueProducersNonSuppressed() {
776        return onlyNonSuppressed(queueProducers.keySet());
777    }
778
779    protected ObjectName[] getTemporaryTopicProducers() {
780        Set<ObjectName> set = temporaryTopicProducers.keySet();
781        return set.toArray(new ObjectName[set.size()]);
782    }
783
784    protected ObjectName[] getTemporaryTopicProducersNonSuppressed() {
785        return onlyNonSuppressed(temporaryTopicProducers.keySet());
786    }
787
788    protected ObjectName[] getTemporaryQueueProducers() {
789        Set<ObjectName> set = temporaryQueueProducers.keySet();
790        return set.toArray(new ObjectName[set.size()]);
791    }
792
793    protected ObjectName[] getTemporaryQueueProducersNonSuppressed() {
794        return onlyNonSuppressed(temporaryQueueProducers.keySet());
795    }
796
797    protected ObjectName[] getDynamicDestinationProducers() {
798        Set<ObjectName> set = dynamicDestinationProducers.keySet();
799        return set.toArray(new ObjectName[set.size()]);
800    }
801
802    protected ObjectName[] getDynamicDestinationProducersNonSuppressed() {
803        return onlyNonSuppressed(dynamicDestinationProducers.keySet());
804    }
805
806    public Broker getContextBroker() {
807        return contextBroker;
808    }
809
810    public void setContextBroker(Broker contextBroker) {
811        this.contextBroker = contextBroker;
812    }
813
814    public ObjectName registerSlowConsumerStrategy(AbortSlowConsumerStrategy strategy) throws MalformedObjectNameException {
815        ObjectName objectName = null;
816        try {
817            objectName = BrokerMBeanSupport.createAbortSlowConsumerStrategyName(brokerObjectName, strategy);
818            if (!registeredMBeans.contains(objectName))  {
819
820                AbortSlowConsumerStrategyView view = null;
821                if (strategy instanceof AbortSlowAckConsumerStrategy) {
822                    view = new AbortSlowAckConsumerStrategyView(this, (AbortSlowAckConsumerStrategy) strategy);
823                } else {
824                    view = new AbortSlowConsumerStrategyView(this, strategy);
825                }
826
827                if (AsyncAnnotatedMBean.registerMBean(asyncInvokeService, mbeanTimeout, managementContext, view, objectName) != null) {
828                    registeredMBeans.add(objectName);
829                }
830            }
831        } catch (Exception e) {
832            LOG.warn("Failed to register MBean {}", strategy);
833            LOG.debug("Failure reason: ", e);
834        }
835        return objectName;
836    }
837
838    public void registerRecoveredTransactionMBean(XATransaction transaction) {
839        try {
840            ObjectName objectName = BrokerMBeanSupport.createXATransactionName(brokerObjectName, transaction);
841            if (!registeredMBeans.contains(objectName))  {
842                RecoveredXATransactionView view = new RecoveredXATransactionView(this, transaction);
843                if (AsyncAnnotatedMBean.registerMBean(asyncInvokeService, mbeanTimeout, managementContext, view, objectName) != null) {
844                    registeredMBeans.add(objectName);
845                }
846            }
847        } catch (Exception e) {
848            LOG.warn("Failed to register prepared transaction MBean {}", transaction);
849            LOG.debug("Failure reason: ", e);
850        }
851    }
852
853    public void unregister(XATransaction transaction) {
854        try {
855            ObjectName objectName = BrokerMBeanSupport.createXATransactionName(brokerObjectName, transaction);
856            if (registeredMBeans.remove(objectName)) {
857                try {
858                    managementContext.unregisterMBean(objectName);
859                } catch (Throwable e) {
860                    LOG.warn("Failed to unregister MBean {}", objectName);
861                    LOG.debug("Failure reason: ", e);
862                }
863            }
864        } catch (Exception e) {
865            LOG.warn("Failed to create object name to unregister {}", transaction, e);
866        }
867    }
868
869    public ObjectName getSubscriberObjectName(Subscription key) {
870        return subscriptionMap.get(key);
871    }
872
873    public Subscription getSubscriber(ObjectName key) {
874        Subscription sub = null;
875        for (Entry<Subscription, ObjectName> entry: subscriptionMap.entrySet()) {
876            if (entry.getValue().equals(key)) {
877                sub = entry.getKey();
878                break;
879            }
880        }
881        return sub;
882    }
883
884    public Map<ObjectName, DestinationView> getQueueViews() {
885        return queues;
886    }
887
888    public Map<ObjectName, DestinationView> getTopicViews() {
889        return topics;
890    }
891
892    public DestinationView getQueueView(String queueName) throws MalformedObjectNameException {
893        ObjectName objName = BrokerMBeanSupport.createDestinationName(brokerObjectName.toString(), "Queue", queueName);
894        return queues.get(objName);
895    }
896
897    public Set<ObjectName> getRegisteredMbeans() {
898        return registeredMBeans;
899    }
900}