View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.jmx.gui;
18  
19  import java.io.IOException;
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.Set;
23  
24  import javax.management.JMException;
25  import javax.management.JMX;
26  import javax.management.MBeanServerConnection;
27  import javax.management.MalformedObjectNameException;
28  import javax.management.ObjectName;
29  import javax.management.remote.JMXConnector;
30  
31  import org.apache.logging.log4j.core.jmx.LoggerContextAdminMBean;
32  import org.apache.logging.log4j.core.jmx.Server;
33  import org.apache.logging.log4j.core.jmx.StatusLoggerAdminMBean;
34  import org.apache.logging.log4j.core.util.Assert;
35  import org.apache.logging.log4j.core.util.Closer;
36  
37  /**
38   * This class allows client-side code to perform operations on remote
39   * (server-side) MBeans via proxies.
40   */
41  public class Client {
42      private JMXConnector connector;
43      private final MBeanServerConnection connection;
44  
45      /**
46       * Constructs a new {@code Client} object and creates proxies for all known
47       * remote MBeans.
48       *
49       * @param connector used to create the MBean server connection through which
50       *            to communicate with the remote mbeans
51       * @throws MalformedObjectNameException if a problem occurred identifying
52       *             one of the remote mbeans
53       * @throws IOException if the connection failed
54       */
55      public Client(final JMXConnector connector) throws MalformedObjectNameException, IOException {
56          this.connector = Assert.requireNonNull(connector, "JMXConnector");
57          this.connector.connect();
58          this.connection = connector.getMBeanServerConnection();
59          init();
60      }
61  
62      /**
63       * Constructs a new {@code Client} object and creates proxies for all known
64       * remote MBeans.
65       *
66       * @param mBeanServerConnection the MBean server connection through which to
67       *            communicate with the remote mbeans
68       * @throws MalformedObjectNameException if a problem occurred identifying
69       *             one of the remote mbeans
70       * @throws IOException if the connection failed
71       */
72      public Client(final MBeanServerConnection mBeanServerConnection) throws MalformedObjectNameException, IOException {
73          this.connection = mBeanServerConnection;
74          init();
75      }
76  
77      private void init() throws MalformedObjectNameException, IOException {
78      }
79  
80      private Set<ObjectName> find(final String pattern) throws JMException, IOException {
81          final ObjectName search = new ObjectName(String.format(pattern, "*"));
82          final Set<ObjectName> result = connection.queryNames(search, null);
83          return result;
84      }
85  
86      /**
87       * Returns a list of proxies that allow operations to be performed on the
88       * remote {@code LoggerContextAdminMBean}s.
89       *
90       * @return a list of proxies to the remote {@code LoggerContextAdminMBean}s
91       * @throws IOException If an I/O error occurred
92       * @throws JMException If a management error occurred
93       */
94      public List<LoggerContextAdminMBean> getLoggerContextAdmins() throws JMException, IOException {
95          final List<LoggerContextAdminMBean> result = new ArrayList<LoggerContextAdminMBean>();
96          final Set<ObjectName> contextNames = find(LoggerContextAdminMBean.PATTERN);
97          for (final ObjectName contextName : contextNames) {
98              result.add(getLoggerContextAdmin(contextName));
99          }
100         return result;
101     }
102 
103     public LoggerContextAdminMBean getLoggerContextAdmin(final ObjectName name) {
104         final LoggerContextAdminMBean ctx = JMX.newMBeanProxy(connection, //
105                 name, //
106                 LoggerContextAdminMBean.class, false);
107         return ctx;
108     }
109 
110     /**
111      * Closes the client connection to its server. Any ongoing or new requests
112      * to the MBeanServerConnection will fail.
113      */
114     public void close() {
115         Closer.closeSilently(connector);
116     }
117 
118     /**
119      * Returns the MBean server connection through which to communicate with the
120      * remote mbeans.
121      *
122      * @return the MBean server connection
123      */
124     public MBeanServerConnection getConnection() {
125         return connection;
126     }
127 
128     /**
129      * Returns the {@code StatusLoggerAdminMBean} associated with the specified
130      * context name, or {@code null}.
131      *
132      * @param contextName search key
133      * @return StatusLoggerAdminMBean or null
134      * @throws MalformedObjectNameException If an object name is malformed
135      * @throws IOException If an I/O error occurred
136      */
137     public StatusLoggerAdminMBean getStatusLoggerAdmin(final String contextName)
138             throws MalformedObjectNameException, IOException {
139         final String pattern = StatusLoggerAdminMBean.PATTERN;
140         final String mbean = String.format(pattern, Server.escape(contextName));
141         final ObjectName search = new ObjectName(mbean);
142         final Set<ObjectName> result = connection.queryNames(search, null);
143         if (result.size() == 0) {
144             return null;
145         }
146         if (result.size() > 1) {
147             System.err.println("WARN: multiple status loggers found for " + contextName + ": " + result);
148         }
149         final StatusLoggerAdminMBean proxy = JMX.newMBeanProxy(connection, //
150                 result.iterator().next(), //
151                 StatusLoggerAdminMBean.class, true); // notificationBroadcaster
152         return proxy;
153     }
154 
155     /**
156      * Returns {@code true} if the specified {@code ObjectName} is for a
157      * {@code LoggerContextAdminMBean}, {@code false} otherwise.
158      *
159      * @param mbeanName the {@code ObjectName} to check.
160      * @return {@code true} if the specified {@code ObjectName} is for a
161      *         {@code LoggerContextAdminMBean}, {@code false} otherwise
162      */
163     public boolean isLoggerContext(final ObjectName mbeanName) {
164         return Server.DOMAIN.equals(mbeanName.getDomain()) //
165                 && mbeanName.getKeyPropertyList().containsKey("type") //
166                 && mbeanName.getKeyPropertyList().size() == 1;
167     }
168 
169     /**
170      * Returns the {@code ObjectName} of the {@code StatusLoggerAdminMBean}
171      * associated with the specified {@code LoggerContextAdminMBean}.
172      *
173      * @param loggerContextObjName the {@code ObjectName} of a
174      *            {@code LoggerContextAdminMBean}
175      * @return {@code ObjectName} of the {@code StatusLoggerAdminMBean}
176      */
177     public ObjectName getStatusLoggerObjectName(final ObjectName loggerContextObjName) {
178         if (!isLoggerContext(loggerContextObjName)) {
179             throw new IllegalArgumentException("Not a LoggerContext: " + loggerContextObjName);
180         }
181         final String cxtName = loggerContextObjName.getKeyProperty("type");
182         final String name = String.format(StatusLoggerAdminMBean.PATTERN, cxtName);
183         try {
184             return new ObjectName(name);
185         } catch (final MalformedObjectNameException ex) {
186             throw new IllegalStateException(name, ex);
187         }
188     }
189 }