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