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.activemq.web;
018    
019    import java.io.IOException;
020    import java.util.ArrayList;
021    import java.util.Collection;
022    import java.util.HashMap;
023    import java.util.List;
024    import java.util.Map;
025    import java.util.Set;
026    
027    import javax.management.MBeanServerConnection;
028    import javax.management.MBeanServerInvocationHandler;
029    import javax.management.MalformedObjectNameException;
030    import javax.management.ObjectName;
031    import javax.management.QueryExp;
032    import javax.management.remote.JMXConnector;
033    import javax.management.remote.JMXConnectorFactory;
034    import javax.management.remote.JMXServiceURL;
035    
036    import org.apache.activemq.broker.jmx.BrokerViewMBean;
037    import org.apache.activemq.broker.jmx.ManagementContext;
038    import org.apache.activemq.broker.jmx.QueueViewMBean;
039    import org.apache.activemq.command.ActiveMQDestination;
040    import org.apache.activemq.web.config.WebConsoleConfiguration;
041    import org.slf4j.Logger;
042    import org.slf4j.LoggerFactory;
043    
044    /**
045     * A {@link BrokerFacade} which uses a JMX-Connection to communicate with a
046     * broker
047     * 
048     * 
049     */
050    public class RemoteJMXBrokerFacade extends BrokerFacadeSupport {
051        
052        private static final transient Logger LOG = LoggerFactory.getLogger(RemoteJMXBrokerFacade.class);
053        
054        private String brokerName;
055        private JMXConnector connector;
056        private WebConsoleConfiguration configuration;
057    
058        public void setBrokerName(String brokerName) {
059            this.brokerName = brokerName;
060        }
061    
062        public WebConsoleConfiguration getConfiguration() {
063                    return configuration;
064            }
065    
066            public void setConfiguration(WebConsoleConfiguration configuration) {
067                    this.configuration = configuration;
068            }
069    
070            /**
071         * Shutdown this facade aka close any open connection.
072         */
073        public void shutdown() {
074            closeConnection();
075        }
076    
077        private ObjectName getBrokerObjectName(MBeanServerConnection connection)
078                            throws IOException, MalformedObjectNameException {
079                    Set<ObjectName> brokers = findBrokers(connection);
080                    if (brokers.size() == 0) {
081                            throw new IOException("No broker could be found in the JMX.");
082                    }
083                    ObjectName name = brokers.iterator().next();
084                    return name;
085            }
086    
087        public BrokerViewMBean getBrokerAdmin() throws Exception {
088            MBeanServerConnection connection = getMBeanServerConnection();
089    
090            Set brokers = findBrokers(connection);
091            if (brokers.size() == 0) {
092                throw new IOException("No broker could be found in the JMX.");
093            }
094            ObjectName name = (ObjectName)brokers.iterator().next();
095            BrokerViewMBean mbean = (BrokerViewMBean)MBeanServerInvocationHandler.newProxyInstance(connection, name, BrokerViewMBean.class, true);
096            return mbean;
097        }
098    
099        public String getBrokerName() throws Exception,
100                            MalformedObjectNameException {
101            return getBrokerAdmin().getBrokerName();
102        }
103        
104        protected MBeanServerConnection getMBeanServerConnection() throws Exception {
105            JMXConnector connector = this.connector;
106            if (isConnectionActive(connector)) {
107                return connector.getMBeanServerConnection();
108            }
109    
110            synchronized (this) {
111                closeConnection();
112    
113                LOG.debug("Creating a new JMX-Connection to the broker");
114                this.connector = createConnection();
115                return this.connector.getMBeanServerConnection();
116            }
117        }
118    
119        protected boolean isConnectionActive(JMXConnector connector) {
120            if (connector == null) {
121                return false;
122            }
123    
124            try {
125                MBeanServerConnection connection = connector.getMBeanServerConnection();
126                int brokerCount = findBrokers(connection).size();
127                return brokerCount > 0;
128            } catch (Exception e) {
129                return false;
130            }
131        }
132    
133        protected JMXConnector createConnection() {
134    
135            Map<String, Object> env = new HashMap<String, Object>();
136                    if (this.configuration.getJmxUser() != null) {
137                            env.put("jmx.remote.credentials", new String[] {
138                                            this.configuration.getJmxUser(),
139                                            this.configuration.getJmxPassword() });
140                    }
141            Collection<JMXServiceURL> jmxUrls = this.configuration.getJmxUrls();
142    
143            Exception exception = null;
144                    for (JMXServiceURL url : jmxUrls) {
145                            try {
146                                    JMXConnector connector = JMXConnectorFactory.connect(url, env);
147                                    connector.connect();
148                                    MBeanServerConnection connection = connector
149                                                    .getMBeanServerConnection();
150    
151                                    Set<ObjectName> brokers = findBrokers(connection);
152                                    if (brokers.size() > 0) {
153                                            LOG.info("Connected via JMX to the broker at " + url);
154                                            return connector;
155                                    }
156                            } catch (Exception e) {
157                                    // Keep the exception for later
158                                    exception = e;
159                            }
160                    }
161                    if (exception != null) {
162                            if (exception instanceof RuntimeException) {
163                                    throw (RuntimeException) exception;
164                            } else {
165                                    throw new RuntimeException(exception);
166                            }
167                    }
168                    throw new IllegalStateException("No broker is found at any of the "
169                                    + jmxUrls.size() + " configured urls");
170            }
171    
172        protected synchronized void closeConnection() {
173            if (connector != null) {
174                try {
175                    LOG.debug("Closing a connection to a broker (" + connector.getConnectionId() + ")");
176    
177                    connector.close();
178                } catch (IOException e) {
179                    // Ignore the exception, since it most likly won't matter
180                    // anymore
181                }
182            }
183        }
184    
185            /**
186             * Finds all ActiveMQ-Brokers registered on a certain JMX-Server or, if a
187             * JMX-BrokerName has been set, the broker with that name.
188             * 
189             * @param connection
190             *            not <code>null</code>
191             * @return Set with ObjectName-elements
192             * @throws IOException
193             * @throws MalformedObjectNameException
194             */
195            @SuppressWarnings("unchecked")
196            protected Set<ObjectName> findBrokers(MBeanServerConnection connection)
197                            throws IOException, MalformedObjectNameException {
198                    ObjectName name;
199                    if (this.brokerName == null) {
200                            name = new ObjectName("org.apache.activemq:type=Broker,brokerName=*");
201                    } else {
202                            name = new ObjectName("org.apache.activemq:brokerName="
203                                            + this.brokerName + ",Type=broker");
204                    }
205    
206                    Set<ObjectName> brokers = connection.queryNames(name, null);
207                    return brokers;
208            }
209            
210            public void purgeQueue(ActiveMQDestination destination) throws Exception {
211                    QueueViewMBean queue = getQueue(destination.getPhysicalName());
212                    queue.purge();
213            }
214            
215            public ManagementContext getManagementContext() {
216                    throw new IllegalStateException("not supported");
217            }
218    
219            
220            @SuppressWarnings("unchecked")
221            protected <T> Collection<T> getManagedObjects(ObjectName[] names,
222                            Class<T> type) {
223                    MBeanServerConnection connection;
224                    try {
225                            connection = getMBeanServerConnection();
226                    } catch (Exception e) {
227                            throw new RuntimeException(e);
228                    }
229    
230                    List<T> answer = new ArrayList<T>();
231                    if (connection != null) {
232                            for (int i = 0; i < names.length; i++) {
233                                    ObjectName name = names[i];
234                                    T value = (T) MBeanServerInvocationHandler.newProxyInstance(
235                                                    connection, name, type, true);
236                                    if (value != null) {
237                                            answer.add(value);
238                                    }
239                            }
240                    }
241                    return answer;
242        }
243    
244        @Override
245        public Set queryNames(ObjectName name, QueryExp query) throws Exception {
246            return getMBeanServerConnection().queryNames(name, query);
247        }
248    
249        @Override
250        public Object newProxyInstance(ObjectName objectName, Class interfaceClass,boolean notificationBroadcaster) throws Exception {
251            return MBeanServerInvocationHandler.newProxyInstance(getMBeanServerConnection(), objectName, interfaceClass, notificationBroadcaster);
252        }
253    
254    }