1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.jmx.gui;
18
19 import java.awt.BorderLayout;
20 import java.awt.Color;
21 import java.awt.Component;
22 import java.awt.Font;
23 import java.awt.event.ActionEvent;
24 import java.io.IOException;
25 import java.io.PrintWriter;
26 import java.io.StringWriter;
27 import java.util.HashMap;
28 import java.util.Map;
29
30 import javax.management.InstanceNotFoundException;
31 import javax.management.JMException;
32 import javax.management.ListenerNotFoundException;
33 import javax.management.MBeanServerDelegate;
34 import javax.management.MBeanServerNotification;
35 import javax.management.MalformedObjectNameException;
36 import javax.management.Notification;
37 import javax.management.NotificationFilterSupport;
38 import javax.management.NotificationListener;
39 import javax.management.ObjectName;
40 import javax.management.remote.JMXConnector;
41 import javax.management.remote.JMXConnectorFactory;
42 import javax.management.remote.JMXServiceURL;
43 import javax.swing.AbstractAction;
44 import javax.swing.JFrame;
45 import javax.swing.JOptionPane;
46 import javax.swing.JPanel;
47 import javax.swing.JScrollPane;
48 import javax.swing.JTabbedPane;
49 import javax.swing.JTextArea;
50 import javax.swing.JToggleButton;
51 import javax.swing.ScrollPaneConstants;
52 import javax.swing.SwingUtilities;
53 import javax.swing.UIManager;
54 import javax.swing.UIManager.LookAndFeelInfo;
55
56 import org.apache.logging.log4j.core.jmx.LoggerContextAdminMBean;
57 import org.apache.logging.log4j.core.jmx.Server;
58 import org.apache.logging.log4j.core.jmx.StatusLoggerAdminMBean;
59 import org.apache.logging.log4j.core.util.Assert;
60
61
62
63
64
65
66
67
68
69
70 public class ClientGui extends JPanel implements NotificationListener {
71 private static final long serialVersionUID = -253621277232291174L;
72 private static final int INITIAL_STRING_WRITER_SIZE = 1024;
73 private final Client client;
74 private final Map<ObjectName, Component> contextObjNameToTabbedPaneMap = new HashMap<ObjectName, Component>();
75 private final Map<ObjectName, JTextArea> statusLogTextAreaMap = new HashMap<ObjectName, JTextArea>();
76 private JTabbedPane tabbedPaneContexts;
77
78 public ClientGui(final Client client) throws IOException, JMException {
79 this.client = Assert.requireNonNull(client, "client");
80 createWidgets();
81 populateWidgets();
82
83
84 final ObjectName addRemoveNotifs = MBeanServerDelegate.DELEGATE_NAME;
85 final NotificationFilterSupport filter = new NotificationFilterSupport();
86 filter.enableType(Server.DOMAIN);
87 client.getConnection().addNotificationListener(addRemoveNotifs, this, null, null);
88 }
89
90 private void createWidgets() {
91 tabbedPaneContexts = new JTabbedPane();
92 this.setLayout(new BorderLayout());
93 this.add(tabbedPaneContexts, BorderLayout.CENTER);
94 }
95
96 private void populateWidgets() throws IOException, JMException {
97 for (final LoggerContextAdminMBean ctx : client.getLoggerContextAdmins()) {
98 addWidgetForLoggerContext(ctx);
99 }
100 }
101
102 private void addWidgetForLoggerContext(final LoggerContextAdminMBean ctx) throws MalformedObjectNameException,
103 IOException, InstanceNotFoundException {
104 final JTabbedPane contextTabs = new JTabbedPane();
105 contextObjNameToTabbedPaneMap.put(ctx.getObjectName(), contextTabs);
106 tabbedPaneContexts.addTab("LoggerContext: " + ctx.getName(), contextTabs);
107
108 final String contextName = ctx.getName();
109 final StatusLoggerAdminMBean status = client.getStatusLoggerAdmin(contextName);
110 if (status != null) {
111 final JTextArea text = createTextArea();
112 final String[] messages = status.getStatusDataHistory();
113 for (final String message : messages) {
114 text.append(message + '\n');
115 }
116 statusLogTextAreaMap.put(ctx.getObjectName(), text);
117 registerListeners(status);
118 final JScrollPane scroll = scroll(text);
119 contextTabs.addTab("StatusLogger", scroll);
120 }
121
122 final ClientEditConfigPanel editor = new ClientEditConfigPanel(ctx);
123 contextTabs.addTab("Configuration", editor);
124 }
125
126 private void removeWidgetForLoggerContext(final ObjectName loggerContextObjName) throws JMException, IOException {
127 final Component tab = contextObjNameToTabbedPaneMap.get(loggerContextObjName);
128 if (tab != null) {
129 tabbedPaneContexts.remove(tab);
130 }
131 statusLogTextAreaMap.remove(loggerContextObjName);
132 final ObjectName objName = client.getStatusLoggerObjectName(loggerContextObjName);
133 try {
134
135 client.getConnection().removeNotificationListener(objName, this);
136 } catch (final ListenerNotFoundException ignored) {
137 }
138 }
139
140 private JTextArea createTextArea() {
141 final JTextArea result = new JTextArea();
142 result.setEditable(false);
143 result.setBackground(this.getBackground());
144 result.setForeground(Color.black);
145 result.setFont(new Font("Monospaced", Font.PLAIN, result.getFont().getSize()));
146 result.setWrapStyleWord(true);
147 return result;
148 }
149
150 private JScrollPane scroll(final JTextArea text) {
151 final JToggleButton toggleButton = new JToggleButton();
152 toggleButton.setAction(new AbstractAction() {
153 private static final long serialVersionUID = -4214143754637722322L;
154
155 @Override
156 public void actionPerformed(final ActionEvent e) {
157 final boolean wrap = toggleButton.isSelected();
158 text.setLineWrap(wrap);
159 }
160 });
161 toggleButton.setToolTipText("Toggle line wrapping");
162 final JScrollPane scrollStatusLog = new JScrollPane(text,
163 ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
164 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
165 scrollStatusLog.setCorner(ScrollPaneConstants.LOWER_RIGHT_CORNER, toggleButton);
166 return scrollStatusLog;
167 }
168
169 private void registerListeners(final StatusLoggerAdminMBean status) throws InstanceNotFoundException,
170 MalformedObjectNameException, IOException {
171 final NotificationFilterSupport filter = new NotificationFilterSupport();
172 filter.enableType(StatusLoggerAdminMBean.NOTIF_TYPE_MESSAGE);
173 final ObjectName objName = status.getObjectName();
174
175 client.getConnection().addNotificationListener(objName, this, filter, status.getContextName());
176 }
177
178 @Override
179 public void handleNotification(final Notification notif, final Object paramObject) {
180 SwingUtilities.invokeLater(new Runnable() {
181 @Override
182 public void run() {
183 handleNotificationInAwtEventThread(notif, paramObject);
184 }
185 });
186 }
187
188 private void handleNotificationInAwtEventThread(final Notification notif, final Object paramObject) {
189 if (StatusLoggerAdminMBean.NOTIF_TYPE_MESSAGE.equals(notif.getType())) {
190 final JTextArea text = statusLogTextAreaMap.get(paramObject);
191 if (text != null) {
192 text.append(notif.getMessage() + '\n');
193 }
194 return;
195 }
196 if (notif instanceof MBeanServerNotification) {
197 final MBeanServerNotification mbsn = (MBeanServerNotification) notif;
198 final ObjectName mbeanName = mbsn.getMBeanName();
199 if (MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(notif.getType())) {
200 onMBeanRegistered(mbeanName);
201 } else if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(notif.getType())) {
202 onMBeanUnregistered(mbeanName);
203 }
204 }
205 }
206
207
208
209
210
211
212 private void onMBeanRegistered(final ObjectName mbeanName) {
213 if (client.isLoggerContext(mbeanName)) {
214 try {
215 final LoggerContextAdminMBean ctx = client.getLoggerContextAdmin(mbeanName);
216 addWidgetForLoggerContext(ctx);
217 } catch (final Exception ex) {
218 handle("Could not add tab for new MBean " + mbeanName, ex);
219 }
220 }
221 }
222
223
224
225
226
227
228 private void onMBeanUnregistered(final ObjectName mbeanName) {
229 if (client.isLoggerContext(mbeanName)) {
230 try {
231 removeWidgetForLoggerContext(mbeanName);
232 } catch (final Exception ex) {
233 handle("Could not remove tab for " + mbeanName, ex);
234 }
235 }
236 }
237
238 private void handle(final String msg, final Exception ex) {
239 System.err.println(msg);
240 ex.printStackTrace();
241
242 final StringWriter sw = new StringWriter(INITIAL_STRING_WRITER_SIZE);
243 ex.printStackTrace(new PrintWriter(sw));
244 JOptionPane.showMessageDialog(this, sw.toString(), msg, JOptionPane.ERROR_MESSAGE);
245 }
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261 public static void main(final String[] args) throws Exception {
262 if (args.length < 1) {
263 usage();
264 return;
265 }
266 String serviceUrl = args[0];
267 if (!serviceUrl.startsWith("service:jmx")) {
268 serviceUrl = "service:jmx:rmi:///jndi/rmi://" + args[0] + "/jmxrmi";
269 }
270 final JMXServiceURL url = new JMXServiceURL(serviceUrl);
271 final Map<String, String> paramMap = new HashMap<String, String>();
272 for (final Object objKey : System.getProperties().keySet()) {
273 final String key = (String) objKey;
274 paramMap.put(key, System.getProperties().getProperty(key));
275 }
276 final JMXConnector connector = JMXConnectorFactory.connect(url, paramMap);
277 final Client client = new Client(connector);
278 final String title = "Log4j JMX Client - " + url;
279
280 SwingUtilities.invokeLater(new Runnable() {
281 @Override
282 public void run() {
283 installLookAndFeel();
284 try {
285 final ClientGui gui = new ClientGui(client);
286 final JFrame frame = new JFrame(title);
287 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
288 frame.getContentPane().add(gui, BorderLayout.CENTER);
289 frame.pack();
290 frame.setVisible(true);
291 } catch (final Exception ex) {
292
293
294
295 ex.printStackTrace();
296
297
298
299 final StringWriter sr = new StringWriter();
300 ex.printStackTrace(new PrintWriter(sr));
301 JOptionPane.showMessageDialog(null, sr.toString(), "Error", JOptionPane.ERROR_MESSAGE);
302 }
303 }
304 });
305 }
306
307 private static void usage() {
308 final String me = ClientGui.class.getName();
309 System.err.println("Usage: java " + me + " <host>:<port>");
310 System.err.println(" or: java " + me + " service:jmx:rmi:///jndi/rmi://<host>:<port>/jmxrmi");
311 final String longAdr = " service:jmx:rmi://<host>:<port>/jndi/rmi://<host>:<port>/jmxrmi";
312 System.err.println(" or: java " + me + longAdr);
313 }
314
315 private static void installLookAndFeel() {
316 try {
317 for (final LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
318 if ("Nimbus".equals(info.getName())) {
319 UIManager.setLookAndFeel(info.getClassName());
320 return;
321 }
322 }
323 } catch (final Exception ex) {
324 ex.printStackTrace();
325 }
326 try {
327 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
328 } catch (final Exception e) {
329 e.printStackTrace();
330 }
331 }
332 }