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.logging.log4j.jmx.gui; 018 019 import java.io.IOException; 020 import java.util.ArrayList; 021 import java.util.List; 022 import java.util.Set; 023 024 import javax.management.JMException; 025 import javax.management.JMX; 026 import javax.management.MBeanServerConnection; 027 import javax.management.MalformedObjectNameException; 028 import javax.management.ObjectName; 029 import javax.management.remote.JMXConnector; 030 031 import org.apache.logging.log4j.core.jmx.LoggerContextAdminMBean; 032 import org.apache.logging.log4j.core.jmx.Server; 033 import org.apache.logging.log4j.core.jmx.StatusLoggerAdminMBean; 034 import org.apache.logging.log4j.core.util.Assert; 035 036 /** 037 * This class allows client-side code to perform operations on remote 038 * (server-side) MBeans via proxies. 039 */ 040 public class Client { 041 private JMXConnector connector; 042 private final MBeanServerConnection connection; 043 044 /** 045 * Constructs a new {@code Client} object and creates proxies for all known 046 * remote MBeans. 047 * 048 * @param connector used to create the MBean server connection through which 049 * to communicate with the remote mbeans 050 * @throws MalformedObjectNameException if a problem occurred identifying 051 * one of the remote mbeans 052 * @throws IOException if the connection failed 053 */ 054 public Client(final JMXConnector connector) throws MalformedObjectNameException, IOException { 055 this.connector = Assert.requireNonNull(connector, "JMXConnector"); 056 this.connector.connect(); 057 this.connection = connector.getMBeanServerConnection(); 058 init(); 059 } 060 061 /** 062 * Constructs a new {@code Client} object and creates proxies for all known 063 * remote MBeans. 064 * 065 * @param mBeanServerConnection the MBean server connection through which to 066 * communicate with the remote mbeans 067 * @throws MalformedObjectNameException if a problem occurred identifying 068 * one of the remote mbeans 069 * @throws IOException if the connection failed 070 */ 071 public Client(final MBeanServerConnection mBeanServerConnection) throws MalformedObjectNameException, IOException { 072 this.connection = mBeanServerConnection; 073 init(); 074 } 075 076 private void init() throws MalformedObjectNameException, IOException { 077 } 078 079 private Set<ObjectName> find(String pattern) throws JMException, IOException { 080 final ObjectName search = new ObjectName(String.format(pattern, "*")); 081 final Set<ObjectName> result = connection.queryNames(search, null); 082 return result; 083 } 084 085 /** 086 * Returns a list of proxies that allow operations to be performed on the 087 * remote {@code LoggerContextAdminMBean}s. 088 * 089 * @return a list of proxies to the remote {@code LoggerContextAdminMBean}s 090 * @throws IOException If an I/O error occurred 091 * @throws JMException If a management error occurred 092 */ 093 public List<LoggerContextAdminMBean> getLoggerContextAdmins() throws JMException, IOException { 094 List<LoggerContextAdminMBean> result = new ArrayList<LoggerContextAdminMBean>(); 095 final Set<ObjectName> contextNames = find(LoggerContextAdminMBean.PATTERN); 096 for (final ObjectName contextName : contextNames) { 097 result.add(getLoggerContextAdmin(contextName)); 098 } 099 return result; 100 } 101 102 public LoggerContextAdminMBean getLoggerContextAdmin(ObjectName name) { 103 final LoggerContextAdminMBean ctx = JMX.newMBeanProxy(connection, // 104 name, // 105 LoggerContextAdminMBean.class, false); 106 return ctx; 107 } 108 109 /** 110 * Closes the client connection to its server. Any ongoing or new requests 111 * to the MBeanServerConnection will fail. 112 */ 113 public void close() { 114 try { 115 connector.close(); 116 } catch (final IOException e) { 117 e.printStackTrace(); 118 } 119 } 120 121 /** 122 * Returns the MBean server connection through which to communicate with the 123 * remote mbeans. 124 * 125 * @return the MBean server connection 126 */ 127 public MBeanServerConnection getConnection() { 128 return connection; 129 } 130 131 /** 132 * Returns the {@code StatusLoggerAdminMBean} associated with the specified 133 * context name, or {@code null}. 134 * 135 * @param contextName search key 136 * @return StatusLoggerAdminMBean or null 137 * @throws MalformedObjectNameException If an object name is malformed 138 * @throws IOException If an I/O error occurred 139 */ 140 public StatusLoggerAdminMBean getStatusLoggerAdmin(String contextName) 141 throws MalformedObjectNameException, IOException { 142 final String pattern = StatusLoggerAdminMBean.PATTERN; 143 final String mbean = String.format(pattern, Server.escape(contextName)); 144 final ObjectName search = new ObjectName(mbean); 145 final Set<ObjectName> result = connection.queryNames(search, null); 146 if (result.size() == 0) { 147 return null; 148 } 149 if (result.size() > 1) { 150 System.err.println("WARN: multiple status loggers found for " + contextName + ": " + result); 151 } 152 final StatusLoggerAdminMBean proxy = JMX.newMBeanProxy(connection, // 153 result.iterator().next(), // 154 StatusLoggerAdminMBean.class, true); // notificationBroadcaster 155 return proxy; 156 } 157 158 /** 159 * Returns {@code true} if the specified {@code ObjectName} is for a 160 * {@code LoggerContextAdminMBean}, {@code false} otherwise. 161 * 162 * @param mbeanName the {@code ObjectName} to check. 163 * @return {@code true} if the specified {@code ObjectName} is for a 164 * {@code LoggerContextAdminMBean}, {@code false} otherwise 165 */ 166 public boolean isLoggerContext(ObjectName mbeanName) { 167 return Server.DOMAIN.equals(mbeanName.getDomain()) // 168 && mbeanName.getKeyPropertyList().containsKey("type") // 169 && mbeanName.getKeyPropertyList().size() == 1; 170 } 171 172 /** 173 * Returns the {@code ObjectName} of the {@code StatusLoggerAdminMBean} 174 * associated with the specified {@code LoggerContextAdminMBean}. 175 * 176 * @param loggerContextObjName the {@code ObjectName} of a 177 * {@code LoggerContextAdminMBean} 178 * @return {@code ObjectName} of the {@code StatusLoggerAdminMBean} 179 */ 180 public ObjectName getStatusLoggerObjectName(ObjectName loggerContextObjName) { 181 if (!isLoggerContext(loggerContextObjName)) { 182 throw new IllegalArgumentException("Not a LoggerContext: " + loggerContextObjName); 183 } 184 final String cxtName = loggerContextObjName.getKeyProperty("type"); 185 final String name = String.format(StatusLoggerAdminMBean.PATTERN, cxtName); 186 try { 187 return new ObjectName(name); 188 } catch (MalformedObjectNameException ex) { 189 throw new IllegalStateException(name, ex); 190 } 191 } 192 }