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 }