1 | |
package kg.apc.jmeter.jmxmon; |
2 | |
|
3 | |
import kg.apc.jmeter.JMeterPluginsUtils; |
4 | |
import kg.apc.jmeter.vizualizers.CorrectedResultCollector; |
5 | |
import org.apache.jmeter.samplers.SampleEvent; |
6 | |
import org.apache.jmeter.samplers.SampleSaveConfiguration; |
7 | |
import org.apache.jmeter.testelement.property.CollectionProperty; |
8 | |
import org.apache.jmeter.testelement.property.JMeterProperty; |
9 | |
import org.apache.jmeter.util.JMeterUtils; |
10 | |
import org.apache.jorphan.logging.LoggingManager; |
11 | |
import org.apache.log.Logger; |
12 | |
|
13 | |
import javax.management.MBeanServerConnection; |
14 | |
import javax.management.remote.JMXConnector; |
15 | |
import javax.management.remote.JMXConnectorFactory; |
16 | |
import javax.management.remote.JMXServiceURL; |
17 | |
import java.io.File; |
18 | |
import java.io.IOException; |
19 | |
import java.net.MalformedURLException; |
20 | |
import java.text.SimpleDateFormat; |
21 | |
import java.util.*; |
22 | |
|
23 | |
public class JMXMonCollector |
24 | |
extends CorrectedResultCollector |
25 | |
implements Runnable, JMXMonSampleGenerator { |
26 | |
|
27 | |
|
28 | |
private static final long serialVersionUID = 1437356057522465756L; |
29 | |
|
30 | |
private static final boolean autoGenerateFiles; |
31 | |
private static final String JMXMON = "JmxMon"; |
32 | 1 | private static final Logger log = LoggingManager.getLoggerForClass(); |
33 | |
public static final String DATA_PROPERTY = "samplers"; |
34 | |
private int interval; |
35 | 13 | private Thread workerThread = null; |
36 | 13 | protected List<JMXMonSampler> jmxMonSamplers = new ArrayList<JMXMonSampler>(); |
37 | 13 | private String autoFileBaseName = null; |
38 | 1 | private static int counter = 0; |
39 | 13 | private String workerHost = null; |
40 | |
|
41 | |
static { |
42 | 1 | autoGenerateFiles = (JMeterUtils.getPropDefault("forceJmxMonFile", "false")).trim().equalsIgnoreCase("true"); |
43 | 1 | } |
44 | |
|
45 | |
private synchronized String getAutoFileName() { |
46 | 0 | String ret = ""; |
47 | 0 | counter++; |
48 | 0 | if (autoFileBaseName == null) { |
49 | 0 | Calendar now = Calendar.getInstance(); |
50 | 0 | SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd-HHmmss"); |
51 | 0 | autoFileBaseName = "jmxMon_" + formatter.format(now.getTime()); |
52 | |
} |
53 | 0 | ret = ret + autoFileBaseName; |
54 | 0 | if (counter > 1) { |
55 | 0 | ret = ret + "_" + counter; |
56 | |
} |
57 | 0 | ret = ret + ".csv"; |
58 | |
|
59 | 0 | return ret; |
60 | |
} |
61 | |
|
62 | 13 | public JMXMonCollector() { |
63 | 13 | interval = JMeterUtils.getPropDefault("jmeterPlugin.jmxmon.interval", 1000); |
64 | 13 | } |
65 | |
|
66 | |
public void setData(CollectionProperty rows) { |
67 | 4 | setProperty(rows); |
68 | 4 | } |
69 | |
|
70 | |
public JMeterProperty getSamplerSettings() { |
71 | 4 | return getProperty(DATA_PROPERTY); |
72 | |
} |
73 | |
|
74 | |
@Override |
75 | |
public synchronized void run() { |
76 | |
try { |
77 | |
while (true) { |
78 | 15 | processConnectors(); |
79 | 15 | this.wait(interval); |
80 | |
} |
81 | 0 | } catch (InterruptedException ex) { |
82 | 0 | log.debug("Monitoring thread was interrupted", ex); |
83 | |
} |
84 | 0 | } |
85 | |
|
86 | |
|
87 | |
private synchronized boolean isWorkingHost(String host) { |
88 | 2 | if (workerHost == null) { |
89 | 2 | workerHost = host; |
90 | 2 | return true; |
91 | |
} else { |
92 | 0 | return host.equals(workerHost); |
93 | |
} |
94 | |
} |
95 | |
|
96 | |
@Override |
97 | |
public void testStarted(String host) { |
98 | |
|
99 | 2 | if (!isWorkingHost(host)) { |
100 | 0 | return; |
101 | |
} |
102 | |
|
103 | |
|
104 | 2 | if (getProperty(FILENAME) == null || getProperty(FILENAME).getStringValue().trim().length() == 0) { |
105 | 2 | if (autoGenerateFiles) { |
106 | 0 | setupSaving(getAutoFileName()); |
107 | |
} else { |
108 | 2 | log.info("JmxMon metrics will not be recorded! Please specify a file name in the gui or run the test with -JforceJmxMonFile=true"); |
109 | |
} |
110 | |
} |
111 | |
try { |
112 | 2 | initiateConnectors(); |
113 | 0 | } catch (MalformedURLException ex) { |
114 | |
|
115 | 0 | log.error("Malformed JMX url", ex); |
116 | 0 | } catch (IOException ex) { |
117 | 0 | log.error("IOException reading JMX", ex); |
118 | 2 | } |
119 | |
|
120 | 2 | workerThread = new Thread(this); |
121 | 2 | workerThread.start(); |
122 | |
|
123 | 2 | super.testStarted(host); |
124 | 2 | } |
125 | |
|
126 | |
private void setupSaving(String fileName) { |
127 | 0 | SampleSaveConfiguration config = getSaveConfig(); |
128 | 0 | JMeterPluginsUtils.doBestCSVSetup(config); |
129 | 0 | setSaveConfig(config); |
130 | 0 | setFilename(fileName); |
131 | 0 | log.info("JMXMon metrics will be stored in " + new File(fileName).getAbsolutePath()); |
132 | 0 | } |
133 | |
|
134 | |
@Override |
135 | |
public void testEnded(String host) { |
136 | 2 | log.debug("Start testEnded"); |
137 | 2 | workerHost = null; |
138 | 2 | if (workerThread == null) { |
139 | 1 | log.debug("End testEnded workerThread == null"); |
140 | 1 | return; |
141 | |
} |
142 | |
|
143 | 1 | workerThread.interrupt(); |
144 | 1 | shutdownConnectors(); |
145 | |
|
146 | |
|
147 | 1 | autoFileBaseName = null; |
148 | 1 | counter = 0; |
149 | 1 | super.testEnded(host); |
150 | 1 | log.debug("End testEnded"); |
151 | 1 | } |
152 | |
|
153 | |
private void initiateConnectors() throws IOException { |
154 | 2 | JMeterProperty prop = getSamplerSettings(); |
155 | 2 | jmxMonSamplers.clear(); |
156 | 2 | if (!(prop instanceof CollectionProperty)) { |
157 | 1 | log.warn("Got unexpected property: " + prop); |
158 | 1 | return; |
159 | |
} |
160 | 1 | CollectionProperty rows = (CollectionProperty) prop; |
161 | |
|
162 | 3 | for (int i = 0; i < rows.size(); i++) { |
163 | 2 | ArrayList<Object> row = (ArrayList<Object>) rows.get(i).getObjectValue(); |
164 | 2 | String label = ((JMeterProperty) row.get(0)).getStringValue(); |
165 | 2 | String jmxUrl = ((JMeterProperty) row.get(1)).getStringValue(); |
166 | 2 | String username = ((JMeterProperty) row.get(2)).getStringValue(); |
167 | 2 | String password = ((JMeterProperty) row.get(3)).getStringValue(); |
168 | 2 | String objectName = ((JMeterProperty) row.get(4)).getStringValue(); |
169 | 2 | String attribute = ((JMeterProperty) row.get(5)).getStringValue(); |
170 | 2 | String key = ((JMeterProperty) row.get(6)).getStringValue(); |
171 | 2 | boolean isDelta = ((JMeterProperty) row.get(7)).getBooleanValue(); |
172 | |
|
173 | 2 | JMXServiceURL u = new JMXServiceURL(jmxUrl); |
174 | 2 | Hashtable attributes = new Hashtable(); |
175 | 2 | String[] buffer = {username, password}; |
176 | 2 | attributes.put("jmx.remote.credentials", (String[]) buffer); |
177 | |
|
178 | 2 | initiateConnector(u, attributes, jmxUrl, label, isDelta, objectName, attribute, key); |
179 | |
} |
180 | 1 | } |
181 | |
|
182 | |
protected void initiateConnector(JMXServiceURL u, Hashtable attributes, String jmxUrl, String name, boolean delta, String objectName, String attribute, String key) throws IOException { |
183 | 0 | JMXConnector jmxConnector = null; |
184 | 0 | MBeanServerConnection mBeanServerConn = findConnectionSameUrl(jmxUrl); |
185 | |
|
186 | 0 | if (mBeanServerConn == null) { |
187 | 0 | log.debug("Create new connection url = " + jmxUrl); |
188 | 0 | jmxConnector = JMXConnectorFactory.connect(u, attributes); |
189 | 0 | mBeanServerConn = jmxConnector.getMBeanServerConnection(); |
190 | |
} else { |
191 | 0 | log.debug("Reused the same connection for url = " + jmxUrl); |
192 | |
} |
193 | |
|
194 | 0 | jmxMonSamplers.add(new JMXMonSampler(mBeanServerConn, jmxConnector, jmxUrl, name, objectName, attribute, key, delta)); |
195 | 0 | } |
196 | |
|
197 | |
private MBeanServerConnection findConnectionSameUrl(String url) { |
198 | 0 | MBeanServerConnection conn = null; |
199 | 0 | boolean continueFind = true; |
200 | 0 | Iterator<JMXMonSampler> it = jmxMonSamplers.iterator(); |
201 | |
|
202 | 0 | while (it.hasNext() && continueFind) { |
203 | 0 | JMXMonSampler jmxSampler = it.next(); |
204 | 0 | String urlTemp = jmxSampler.getUrl(); |
205 | 0 | if (urlTemp != null && urlTemp.equals(url)) { |
206 | 0 | conn = jmxSampler.getRemote(); |
207 | 0 | continueFind = false; |
208 | |
} |
209 | 0 | } |
210 | |
|
211 | 0 | return conn; |
212 | |
} |
213 | |
|
214 | |
private void shutdownConnectors() { |
215 | 1 | log.debug("Start shutdownConnectors"); |
216 | |
|
217 | 1 | for (JMXMonSampler jmxSampler : jmxMonSamplers) { |
218 | 2 | JMXConnector jmxConnector = jmxSampler.getJmxConnector(); |
219 | 2 | if (jmxConnector != null) { |
220 | |
try { |
221 | 0 | jmxConnector.close(); |
222 | 0 | log.debug("jmx connector is closed"); |
223 | 0 | } catch (Exception ex) { |
224 | 0 | log.debug("Can't close jmx connector, but continue"); |
225 | 0 | } |
226 | |
} else { |
227 | 2 | log.debug("jmxConnector == null, don't try to close connection"); |
228 | |
} |
229 | 2 | } |
230 | 1 | jmxMonSamplers.clear(); |
231 | 1 | log.debug("End shutdownConnectors"); |
232 | 1 | } |
233 | |
|
234 | |
protected void processConnectors() { |
235 | 19 | for (JMXMonSampler sampler : jmxMonSamplers) { |
236 | 6 | sampler.generateSamples(this); |
237 | 6 | } |
238 | 19 | } |
239 | |
|
240 | |
@Override |
241 | |
public void sampleOccurred(SampleEvent event) { |
242 | |
|
243 | 6 | } |
244 | |
|
245 | |
protected void jmxMonSampleOccurred(SampleEvent event) { |
246 | 2 | super.sampleOccurred(event); |
247 | 2 | } |
248 | |
|
249 | |
@Override |
250 | |
public void generateSample(double value, String label) { |
251 | 6 | JMXMonSampleResult res = new JMXMonSampleResult(); |
252 | 6 | res.setSampleLabel(label); |
253 | 6 | res.setValue(value); |
254 | 6 | res.setSuccessful(true); |
255 | 6 | SampleEvent e = new SampleEvent(res, JMXMON); |
256 | 6 | jmxMonSampleOccurred(e); |
257 | 6 | } |
258 | |
} |