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 }