1 package org.cyclopsgroup.jmxterm.cmd;
2
3 import org.apache.commons.lang3.Validate;
4 import org.cyclopsgroup.jcli.annotation.Argument;
5 import org.cyclopsgroup.jcli.annotation.Cli;
6 import org.cyclopsgroup.jcli.annotation.MultiValue;
7 import org.cyclopsgroup.jcli.annotation.Option;
8 import org.cyclopsgroup.jmxterm.Command;
9 import org.cyclopsgroup.jmxterm.Session;
10 import org.cyclopsgroup.jmxterm.io.CommandOutput;
11 import org.cyclopsgroup.jmxterm.io.JlineCommandInput;
12 import org.jline.reader.impl.LineReaderImpl;
13
14 import javax.management.JMException;
15 import javax.management.MBeanAttributeInfo;
16 import javax.management.MBeanServerConnection;
17 import javax.management.ObjectName;
18 import java.io.IOException;
19 import java.text.FieldPosition;
20 import java.text.MessageFormat;
21 import java.util.ArrayList;
22 import java.util.Date;
23 import java.util.List;
24 import java.util.concurrent.Executors;
25 import java.util.concurrent.ScheduledExecutorService;
26 import java.util.concurrent.TimeUnit;
27
28
29
30
31
32
33 @Cli(name = "watch", description = "Watch the value of one MBean attribute constantly",
34 note = "DO NOT call this command in a script and expect decent output")
35 public class WatchCommand extends Command {
36 private static class ConsoleOutput extends Output {
37 private final LineReaderImpl console;
38
39 private ConsoleOutput(Session session) {
40 if (!(session.getInput() instanceof JlineCommandInput)) {
41 throw new IllegalStateException("Under current context, watch command can't execute.");
42 }
43 this.console = ((JlineCommandInput) session.getInput()).getConsole();
44 }
45
46 void printLine(String line) throws IOException {
47 console.redrawLine();
48 console.getTerminal().writer().print(line);
49 console.flush();
50 }
51 }
52
53 private static abstract class Output {
54 abstract void printLine(String line) throws IOException;
55 }
56
57 private static class ReportOutput extends Output {
58 private final CommandOutput out;
59
60 private ReportOutput(Session session) {
61 this.out = session.output;
62 }
63
64 @Override
65 void printLine(String line) {
66 out.println(line);
67 }
68
69 }
70
71 private static final String BUILDING_ATTRIBUTE_NOW = "%now";
72
73 private static final int DEFAULT_REFRESH_INTERVAL = 1;
74
75 private List<String> attributes = new ArrayList<String>();
76
77 private String outputFormat;
78
79 private int refreshInterval = DEFAULT_REFRESH_INTERVAL;
80
81 private boolean report;
82
83 private int stopAfter;
84
85 @Override
86 public List<String> doSuggestArgument() throws IOException, JMException {
87 if (getSession().getBean() != null) {
88 MBeanServerConnection con = getSession().getConnection().getServerConnection();
89 MBeanAttributeInfo[] ais =
90 con.getMBeanInfo(new ObjectName(getSession().getBean())).getAttributes();
91 List<String> results = new ArrayList<String>(ais.length);
92 for (MBeanAttributeInfo ai : ais) {
93 results.add(ai.getName());
94 }
95 results.add(BUILDING_ATTRIBUTE_NOW);
96 return results;
97 }
98 return null;
99 }
100
101 @Override
102 public void execute() throws IOException, JMException {
103 if (report && stopAfter == 0) {
104 throw new IllegalArgumentException(
105 "When --report is sepcified, --stopafter(-s) must be specificed");
106 }
107 Session session = getSession();
108 String domain = DomainCommand.getDomainName(null, session);
109 if (domain == null) {
110 throw new IllegalStateException("Please specify a domain using domain command first.");
111 }
112 String beanName = BeanCommand.getBeanName(null, domain, session);
113 if (beanName == null) {
114 throw new IllegalStateException("Please specify a bean using bean command first.");
115 }
116
117 final ObjectName name = new ObjectName(beanName);
118 final MBeanServerConnection con = session.getConnection().getServerConnection();
119 final Output output;
120 if (report) {
121 output = new ReportOutput(session);
122 } else {
123 output = new ConsoleOutput(session);
124 getSession().output.printMessage("press any key to stop. DO NOT press Ctrl+C !!!");
125 }
126
127 final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
128 executor.scheduleWithFixedDelay(new Runnable() {
129 public void run() {
130 try {
131 printValues(name, con, output);
132 } catch (IOException e) {
133 getSession().output.printError(e);
134 }
135 }
136 }, 0, refreshInterval, TimeUnit.SECONDS);
137 if (stopAfter > 0) {
138 executor.schedule(new Runnable() {
139 public void run() {
140 executor.shutdownNow();
141 }
142 }, stopAfter, TimeUnit.SECONDS);
143 }
144 if (!report) {
145 System.in.read();
146 System.out.println();
147 executor.shutdownNow();
148 }
149
150 session.output.println("");
151 }
152
153 private Object getAttributeValue(ObjectName beanName, String attributeName,
154 MBeanServerConnection connection) throws IOException {
155
156 if (attributeName.equals(BUILDING_ATTRIBUTE_NOW)) {
157 return new Date();
158 }
159 try {
160 return connection.getAttribute(beanName, attributeName);
161 } catch (JMException e) {
162 return e.getClass().getSimpleName();
163 }
164 }
165
166 private void printValues(ObjectName beanName, MBeanServerConnection connection, Output output)
167 throws IOException {
168 StringBuffer result = new StringBuffer();
169 if (outputFormat == null) {
170 boolean first = true;
171 for (String attributeName : attributes) {
172 if (first) {
173 first = false;
174 } else {
175 result.append(", ");
176 }
177 result.append(getAttributeValue(beanName, attributeName, connection));
178 }
179 } else {
180 Object[] values = new Object[attributes.size()];
181 int i = 0;
182 for (String attributeNamne : attributes) {
183 values[i++] = getAttributeValue(beanName, attributeNamne, connection);
184 }
185 MessageFormat format = new MessageFormat(outputFormat);
186 format.format(values, result, new FieldPosition(0));
187 }
188 output.printLine(result.toString());
189 }
190
191
192
193
194 @MultiValue(listType = ArrayList.class, minValues = 1)
195 @Argument(displayName = "attr", description = "Name of attributes to watch")
196 public final void setAttributes(List<String> attributes) {
197 this.attributes = attributes;
198 }
199
200
201
202
203 @Option(name = "f", longName = "format", displayName = "expr",
204 description = "Java pattern(java.text.MessageFormat) to print attribute values")
205 public final void setOutputFormat(String outputFormat) {
206 this.outputFormat = outputFormat;
207 }
208
209
210
211
212 @Option(name = "i", longName = "interval", displayName = "sec",
213 description = "Optional number of seconds between consecutive poll, default is 1 second",
214 defaultValue = "1")
215 public final void setRefreshInterval(int refreshInterval) {
216 Validate.isTrue(refreshInterval > 0, "Invalid interval value " + refreshInterval);
217 this.refreshInterval = refreshInterval;
218 }
219
220
221
222
223 @Option(name = "r", longName = "report", description = "Output result line by line as report")
224 public final void setReport(boolean report) {
225 this.report = report;
226 }
227
228
229
230
231 @Option(name = "s", longName = "stopafter", displayName = "sec",
232 description = "Stop after watching a number of seconds")
233 public final void setStopAfter(int stopAfter) {
234 Validate.isTrue(stopAfter >= 0, "Invalid stop after argument " + stopAfter);
235 this.stopAfter = stopAfter;
236 }
237 }