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  
36  /**
37   * This class allows client-side code to perform operations on remote
38   * (server-side) MBeans via proxies.
39   */
40  public class Client {
41      private JMXConnector connector;
42      private final MBeanServerConnection connection;
43  
44      /**
45       * Constructs a new {@code Client} object and creates proxies for all known
46       * remote MBeans.
47       * 
48       * @param connector used to create the MBean server connection through which
49       *            to communicate with the remote mbeans
50       * @throws MalformedObjectNameException if a problem occurred identifying
51       *             one of the remote mbeans
52       * @throws IOException if the connection failed
53       */
54      public Client(final JMXConnector connector) throws MalformedObjectNameException, IOException {
55          this.connector = Assert.requireNonNull(connector, "JMXConnector");
56          this.connector.connect();
57          this.connection = connector.getMBeanServerConnection();
58          init();
59      }
60  
61      /**
62       * Constructs a new {@code Client} object and creates proxies for all known
63       * remote MBeans.
64       * 
65       * @param mBeanServerConnection the MBean server connection through which to
66       *            communicate with the remote mbeans
67       * @throws MalformedObjectNameException if a problem occurred identifying
68       *             one of the remote mbeans
69       * @throws IOException if the connection failed
70       */
71      public Client(final MBeanServerConnection mBeanServerConnection) throws MalformedObjectNameException, IOException {
72          this.connection = mBeanServerConnection;
73          init();
74      }
75  
76      private void init() throws MalformedObjectNameException, IOException {
77      }
78  
79      private Set<ObjectName> find(String pattern) throws JMException, IOException {
80          final ObjectName search = new ObjectName(String.format(pattern, "*"));
81          final Set<ObjectName> result = connection.queryNames(search, null);
82          return result;
83      }
84  
85      /**
86       * Returns a list of proxies that allow operations to be performed on the
87       * remote {@code LoggerContextAdminMBean}s.
88       * 
89       * @return a list of proxies to the remote {@code LoggerContextAdminMBean}s
90       * @throws IOException If an I/O error occurred
91       * @throws JMException If a management error occurred
92       */
93      public List<LoggerContextAdminMBean> getLoggerContextAdmins() throws JMException, IOException {
94          List<LoggerContextAdminMBean> result = new ArrayList<LoggerContextAdminMBean>();
95          final Set<ObjectName> contextNames = find(LoggerContextAdminMBean.PATTERN);
96          for (final ObjectName contextName : contextNames) {
97              result.add(getLoggerContextAdmin(contextName));
98          }
99          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 }