blob: fe513567f591d97aef5b721edb211f389cb4b3e6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.jmx.gui;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.management.JMException;
import javax.management.JMX;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import org.apache.logging.log4j.core.jmx.LoggerContextAdminMBean;
import org.apache.logging.log4j.core.jmx.Server;
import org.apache.logging.log4j.core.jmx.StatusLoggerAdminMBean;
import org.apache.logging.log4j.core.util.Closer;
/**
* This class allows client-side code to perform operations on remote
* (server-side) MBeans via proxies.
*/
public class Client {
private JMXConnector connector;
private final MBeanServerConnection connection;
/**
* Constructs a new {@code Client} object and creates proxies for all known
* remote MBeans.
*
* @param connector used to create the MBean server connection through which
* to communicate with the remote mbeans
* @throws MalformedObjectNameException if a problem occurred identifying
* one of the remote mbeans
* @throws IOException if the connection failed
*/
public Client(final JMXConnector connector) throws MalformedObjectNameException, IOException {
this.connector = Objects.requireNonNull(connector, "JMXConnector");
this.connector.connect();
this.connection = connector.getMBeanServerConnection();
init();
}
/**
* Constructs a new {@code Client} object and creates proxies for all known
* remote MBeans.
*
* @param mBeanServerConnection the MBean server connection through which to
* communicate with the remote mbeans
* @throws MalformedObjectNameException if a problem occurred identifying
* one of the remote mbeans
* @throws IOException if the connection failed
*/
public Client(final MBeanServerConnection mBeanServerConnection) throws MalformedObjectNameException, IOException {
this.connection = mBeanServerConnection;
init();
}
private void init() throws MalformedObjectNameException, IOException {
}
private Set<ObjectName> find(final String pattern) throws JMException, IOException {
final ObjectName search = new ObjectName(String.format(pattern, "*"));
final Set<ObjectName> result = connection.queryNames(search, null);
return result;
}
/**
* Returns a list of proxies that allow operations to be performed on the
* remote {@code LoggerContextAdminMBean}s.
*
* @return a list of proxies to the remote {@code LoggerContextAdminMBean}s
* @throws IOException If an I/O error occurred
* @throws JMException If a management error occurred
*/
public List<LoggerContextAdminMBean> getLoggerContextAdmins() throws JMException, IOException {
final List<LoggerContextAdminMBean> result = new ArrayList<>();
final Set<ObjectName> contextNames = find(LoggerContextAdminMBean.PATTERN);
for (final ObjectName contextName : contextNames) {
result.add(getLoggerContextAdmin(contextName));
}
return result;
}
public LoggerContextAdminMBean getLoggerContextAdmin(final ObjectName name) {
final LoggerContextAdminMBean ctx = JMX.newMBeanProxy(connection, //
name, //
LoggerContextAdminMBean.class, false);
return ctx;
}
/**
* Closes the client connection to its server. Any ongoing or new requests
* to the MBeanServerConnection will fail.
*/
public void close() {
Closer.closeSilently(connector);
}
/**
* Returns the MBean server connection through which to communicate with the
* remote mbeans.
*
* @return the MBean server connection
*/
public MBeanServerConnection getConnection() {
return connection;
}
/**
* Returns the {@code StatusLoggerAdminMBean} associated with the specified
* context name, or {@code null}.
*
* @param contextName search key
* @return StatusLoggerAdminMBean or null
* @throws MalformedObjectNameException If an object name is malformed
* @throws IOException If an I/O error occurred
*/
public StatusLoggerAdminMBean getStatusLoggerAdmin(final String contextName)
throws MalformedObjectNameException, IOException {
final String pattern = StatusLoggerAdminMBean.PATTERN;
final String mbean = String.format(pattern, Server.escape(contextName));
final ObjectName search = new ObjectName(mbean);
final Set<ObjectName> result = connection.queryNames(search, null);
if (result.size() == 0) {
return null;
}
if (result.size() > 1) {
System.err.println("WARN: multiple status loggers found for " + contextName + ": " + result);
}
final StatusLoggerAdminMBean proxy = JMX.newMBeanProxy(connection, //
result.iterator().next(), //
StatusLoggerAdminMBean.class, true); // notificationBroadcaster
return proxy;
}
/**
* Returns {@code true} if the specified {@code ObjectName} is for a
* {@code LoggerContextAdminMBean}, {@code false} otherwise.
*
* @param mbeanName the {@code ObjectName} to check.
* @return {@code true} if the specified {@code ObjectName} is for a
* {@code LoggerContextAdminMBean}, {@code false} otherwise
*/
public boolean isLoggerContext(final ObjectName mbeanName) {
return Server.DOMAIN.equals(mbeanName.getDomain()) //
&& mbeanName.getKeyPropertyList().containsKey("type") //
&& mbeanName.getKeyPropertyList().size() == 1;
}
/**
* Returns the {@code ObjectName} of the {@code StatusLoggerAdminMBean}
* associated with the specified {@code LoggerContextAdminMBean}.
*
* @param loggerContextObjName the {@code ObjectName} of a
* {@code LoggerContextAdminMBean}
* @return {@code ObjectName} of the {@code StatusLoggerAdminMBean}
*/
public ObjectName getStatusLoggerObjectName(final ObjectName loggerContextObjName) {
if (!isLoggerContext(loggerContextObjName)) {
throw new IllegalArgumentException("Not a LoggerContext: " + loggerContextObjName);
}
final String cxtName = loggerContextObjName.getKeyProperty("type");
final String name = String.format(StatusLoggerAdminMBean.PATTERN, cxtName);
try {
return new ObjectName(name);
} catch (final MalformedObjectNameException ex) {
throw new IllegalStateException(name, ex);
}
}
}