Skip to content

Commit 1464ec6

Browse files
committed
Wildcard support implemented, readme updated
Exporter now is using single queue manager connection for all activities Resolves: #144, #145
1 parent 648e0c3 commit 1464ec6

File tree

18 files changed

+449
-257
lines changed

18 files changed

+449
-257
lines changed

README.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ Supports [IBM MQ](https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/co
5555

5656
Was tested on MQ ver.9.0.x.x and MQ ver. 9.1.x.x.
5757

58+
**Note:** The Publish/Subscribe Mode "[PSMODE](https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/com.ibm.mq.con.doc/q017110_.htm)" Queue Manager attribute in IBM MQ should have value "enabled".
59+
5860
#### Dependencies
5961
<sub><sup> [Back to TOC.](#table-of-contents) </sup></sub><br/>
6062
List of dependencies:
@@ -73,7 +75,7 @@ qmgrConnectionParams:
7375
# Queue manager name.
7476
qmgrName: QM
7577
# Queue manager host.
76-
qmgrHost: hostname
78+
qmgrHost: localhost
7779
# Queue manager connection port.
7880
qmgrPort: 1414
7981
# Queue manager connection channel.
@@ -128,20 +130,29 @@ PCFParameters:
128130
# only one PCF command will be sent. But response will contain metrics for all 10.000 queues and that will lead to performance problems.
129131
usePCFWildcards: true
130132
# Interval in seconds between sending PCF commands.
131-
scrapeInterval: 10
133+
scrapeInterval: 300
134+
135+
# Further block contains info about monitoring objects. It supports "*" wildcard at the end of the name.
136+
# Firstly, objects from "include" section are retrieved.
137+
# Then objects from "exclude" section are retrieved.
138+
# Finally, objects that are in the first group but not in the second are added to the monitoring list.
132139

133140
# Monitored queues.
134141
queues:
135-
- QUEUE1
136-
- QUEUE2
142+
include:
143+
- '*'
144+
exclude:
145+
- SYSTEM.*
137146

138147
# Monitored listeners.
139148
listeners:
140-
- LISTENER01
141-
149+
include:
150+
exclude:
151+
- SYSTEM.*
152+
142153
# Monitored channels.
143154
channels:
144-
- MANAGEMENT.CHANNEL
155+
include:
145156
```
146157
#### Build
147158
<sub><sup> [Back to TOC.](#table-of-contents) </sup></sub><br/>

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>ru.cinimex</groupId>
66
<artifactId>mq-java-exporter</artifactId>
7-
<version>1.0-SNAPSHOT</version>
7+
<version>0.4.0-rc1</version>
88
<!-- Output to jar format -->
99
<packaging>jar</packaging>
1010
<properties>

src/main/java/ru/cinimex/exporter/Config.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
import java.io.File;
1010
import java.io.FileNotFoundException;
1111
import java.io.FileReader;
12-
import java.util.ArrayList;
1312
import java.util.HashMap;
1413
import java.util.LinkedHashMap;
1514
import java.util.List;
15+
import java.util.Map;
1616

1717
/**
1818
* Class is used for parsing config file.
@@ -29,9 +29,9 @@ public class Config {
2929
private int connTimeout;
3030
private int endpPort;
3131
private String endpURL;
32-
private ArrayList<String> queues;
33-
private ArrayList<String> channels;
34-
private ArrayList<String> listeners;
32+
private Map<String, List<String>> queues;
33+
private Map<String, List<String>> channels;
34+
private Map<String, List<String>> listeners;
3535
private boolean sendPCFCommands;
3636
private boolean usePCFWildcards;
3737
private int scrapeInterval;
@@ -59,9 +59,9 @@ public Config(String path) {
5959
this.password = (String) qmgrConnectionParams.get("password");
6060
this.mqscp = (boolean) qmgrConnectionParams.get("mqscp");
6161
this.connTimeout = (Integer) qmgrConnectionParams.get("connTimeout");
62-
queues = (ArrayList<String>) config.get("queues");
63-
listeners = (ArrayList<String>) config.get("listeners");
64-
channels = (ArrayList<String>) config.get("channels");
62+
this.queues = (Map<String, List<String>>) config.get("queues");
63+
this.listeners = (Map<String, List<String>>) config.get("listeners");
64+
this.channels = (Map<String, List<String>>) config.get("channels");
6565
this.endpPort = (Integer) prometheusEndpointParams.get("port");
6666
this.endpURL = (String) (prometheusEndpointParams.get("url"));
6767
this.sendPCFCommands = (boolean) pcfParameters.get("sendPCFCommands");
@@ -101,11 +101,11 @@ public boolean usePCFWildcards() {
101101
return usePCFWildcards;
102102
}
103103

104-
public List<String> getChannels() {
104+
public Map<String, List<String>> getChannels() {
105105
return channels;
106106
}
107107

108-
public List<String> getListeners() {
108+
public Map<String, List<String>> getListeners() {
109109
return listeners;
110110
}
111111

@@ -145,7 +145,7 @@ public String getEndpURL() {
145145
return endpURL;
146146
}
147147

148-
public List<String> getQueues() {
148+
public Map<String, List<String>> getQueues() {
149149
return queues;
150150
}
151151

src/main/java/ru/cinimex/exporter/ExporterLauncher.java

Lines changed: 89 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.ibm.mq.MQTopic;
77
import com.ibm.mq.constants.MQConstants;
88
import com.ibm.mq.pcf.PCFMessage;
9+
import com.ibm.mq.pcf.PCFMessageAgent;
910
import org.apache.logging.log4j.LogManager;
1011
import org.apache.logging.log4j.Logger;
1112
import ru.cinimex.exporter.mq.MQConnection;
@@ -24,46 +25,33 @@
2425
import java.util.ArrayList;
2526
import java.util.List;
2627

28+
import static ru.cinimex.exporter.mq.MQConnection.createMQConnectionParams;
29+
2730
/**
2831
* Main class of mq exporter tool. Parses config, scans topics, starts subscribers.
2932
*/
3033
public class ExporterLauncher {
3134
private static final Logger logger = LogManager.getLogger(ExporterLauncher.class);
3235
private static final String TOPIC_STRING = "$SYS/MQ/INFO/QMGR/%s/Monitor/METADATA/CLASSES";
3336
private static final int GMO = MQConstants.MQGMO_WAIT | MQConstants.MQGMO_COMPLETE_MSG | MQConstants.MQGMO_SYNCPOINT;
34-
private static MQSubscriberManager manager;
37+
private static MQSubscriberManager manager;
3538
private static HTTPServer server;
3639

37-
public static void main(String[] args) {
40+
public static void main(String[] args) throws MQException, IOException {
3841
if (args.length == 0) {
3942
logger.error("It seems like you forgot to specify path to the config file.");
4043
System.exit(1);
4144
}
4245
Config config = new Config(args[0]);
46+
MQConnection.establish(config.getQmgrName(), createMQConnectionParams(config));
4347

4448
createShutdownHook();
4549
ArrayList<PCFElement> elements = getAllPublishedMetrics(config);
4650
ArrayList<MQObject.MQType> monitoringTypes = new ArrayList<>();
47-
ArrayList<MQObject> objects = new ArrayList<>();
48-
49-
if (config.getQueues() != null && !config.getQueues().isEmpty()) {
50-
monitoringTypes.add(MQObject.MQType.QUEUE);
51-
for (String queueName : config.getQueues()) {
52-
objects.add(new MQObject(queueName, MQObject.MQType.QUEUE));
53-
}
54-
}
55-
if (config.getChannels() != null && !config.getChannels().isEmpty()) {
56-
monitoringTypes.add(MQObject.MQType.CHANNEL);
57-
for (String channelName : config.getChannels()) {
58-
objects.add(new MQObject(channelName, MQObject.MQType.CHANNEL));
59-
}
60-
}
61-
if (config.getListeners() != null && !config.getListeners().isEmpty()) {
62-
monitoringTypes.add(MQObject.MQType.LISTENER);
63-
for (String listenerName : config.getListeners()) {
64-
objects.add(new MQObject(listenerName, MQObject.MQType.LISTENER));
65-
}
66-
}
51+
List<MQObject> objects = getMonitoringObjects(config);
52+
monitoringTypes.add(MQObject.MQType.QUEUE);
53+
monitoringTypes.add(MQObject.MQType.CHANNEL);
54+
monitoringTypes.add(MQObject.MQType.LISTENER);
6755

6856
MetricsManager.initMetrics(elements, monitoringTypes);
6957
manager = new MQSubscriberManager(config);
@@ -83,27 +71,26 @@ public static void main(String[] args) {
8371
* @return - array, filled with metrics headers.
8472
*/
8573
private static ArrayList<PCFElement> getAllPublishedMetrics(Config config) {
86-
MQConnection connection = new MQConnection();
8774
MQTopic topic = null;
8875
ArrayList<PCFElement> elements = new ArrayList<>();
8976
MQGetMessageOptions gmo = new MQGetMessageOptions();
9077
gmo.options = GMO;
9178
gmo.waitInterval = 30000;
9279
try {
93-
connection.establish(config.getQmgrName(), MQConnection.createMQConnectionParams(config));
94-
topic = connection.createTopic(String.format(TOPIC_STRING, config.getQmgrName()));
80+
String qmgrName = config.getQmgrName();
81+
topic = MQConnection.createTopic(String.format(TOPIC_STRING, qmgrName));
9582
MQMessage msg = getEmptyMessage();
9683
topic.get(msg, gmo);
9784
PCFMessage pcfResponse = new PCFMessage(msg);
9885
List<PCFClass> classes = PCFDataParser.getPCFClasses(pcfResponse);
9986
for (PCFClass pcfClass : classes) {
100-
topic = connection.createTopic(pcfClass.getTopicString());
87+
topic = MQConnection.createTopic(pcfClass.getTopicString());
10188
msg = getEmptyMessage();
10289
topic.get(msg, gmo);
10390
pcfResponse = new PCFMessage(msg);
10491
List<PCFType> types = PCFDataParser.getPCFTypes(pcfResponse);
10592
for (PCFType type : types) {
106-
topic = connection.createTopic(type.getTopicString());
93+
topic = MQConnection.createTopic(type.getTopicString());
10794
msg = getEmptyMessage();
10895
topic.get(msg, gmo);
10996
pcfResponse = new PCFMessage(msg);
@@ -118,14 +105,87 @@ private static ArrayList<PCFElement> getAllPublishedMetrics(Config config) {
118105
if (topic != null && topic.isOpen()) {
119106
topic.close();
120107
}
121-
connection.close();
122108
} catch (MQException e) {
123109
logger.error("Error occurred during disconnecting from topic {}. Error: ", topic.toString(), e);
124110
}
125111
}
126112
return elements;
127113
}
128114

115+
private static List<MQObject> getMonitoringObjects(Config config) throws MQException, IOException {
116+
List<MQObject> objects = new ArrayList<>();
117+
118+
for (MQObject.MQType type : MQObject.MQType.values()) {
119+
switch (type) {
120+
case QUEUE:
121+
objects.addAll(inquireMQObjectsByPatterns(config.getQueues().get("include"), type, MQConstants.MQCACF_Q_NAMES));
122+
objects.removeAll(inquireMQObjectsByPatterns(config.getQueues().get("exclude"), type, MQConstants.MQCACF_Q_NAMES));
123+
break;
124+
case CHANNEL:
125+
objects.addAll(inquireMQObjectsByPatterns(config.getChannels().get("include"), type, MQConstants.MQCACH_CHANNEL_NAMES));
126+
objects.removeAll(inquireMQObjectsByPatterns(config.getChannels().get("exclude"), type, MQConstants.MQCACH_CHANNEL_NAMES));
127+
break;
128+
case LISTENER:
129+
objects.addAll(inquireMQObjectsByPatterns(config.getListeners().get("include"), type, MQConstants.MQCACH_LISTENER_NAME));
130+
objects.removeAll(inquireMQObjectsByPatterns(config.getListeners().get("exclude"), type, MQConstants.MQCACH_LISTENER_NAME));
131+
break;
132+
}
133+
}
134+
return objects;
135+
}
136+
137+
private static List<MQObject> inquireMQObjectsByPatterns(List<String> rules, MQObject.MQType type, int getValueParam) throws MQException, IOException {
138+
139+
List<MQObject> objects = new ArrayList<>();
140+
if (rules != null && !rules.isEmpty()) {
141+
PCFMessageAgent agent = new PCFMessageAgent();
142+
agent.connect(MQConnection.getQueueManager());
143+
for (String rule : rules) {
144+
PCFMessage pcfCommand = preparePCFCommand(type, rule);
145+
PCFMessage[] pcfResponse = agent.send(pcfCommand);
146+
if (!type.equals(MQObject.MQType.LISTENER)) {
147+
String[] names = (String[]) pcfResponse[0].getParameterValue(getValueParam);
148+
149+
for (int index = 0; index < names.length; index++) {
150+
String objName = names[index].trim();
151+
if (!objName.startsWith("AMQ.")) {
152+
objects.add(new MQObject(objName, type));
153+
}
154+
}
155+
} else {
156+
for (int index = 0; index < pcfResponse.length; index++) {
157+
objects.add(new MQObject(pcfResponse[index].getParameterValue(MQConstants.MQCACH_LISTENER_NAME).toString().trim(), type));
158+
}
159+
}
160+
}
161+
agent.disconnect();
162+
}
163+
164+
return objects;
165+
}
166+
167+
private static PCFMessage preparePCFCommand(MQObject.MQType type, String name) {
168+
PCFMessage command;
169+
switch (type) {
170+
case QUEUE:
171+
command = new PCFMessage(MQConstants.MQCMD_INQUIRE_Q_NAMES);
172+
command.addParameter(MQConstants.MQCA_Q_NAME, name);
173+
command.addParameter(MQConstants.MQIA_Q_TYPE, MQConstants.MQQT_LOCAL);
174+
break;
175+
case CHANNEL:
176+
command = new PCFMessage(MQConstants.MQCMD_INQUIRE_CHANNEL_NAMES);
177+
command.addParameter(MQConstants.MQCACH_CHANNEL_NAME, name);
178+
break;
179+
case LISTENER:
180+
command = new PCFMessage(MQConstants.MQCMD_INQUIRE_LISTENER);
181+
command.addParameter(MQConstants.MQCACH_LISTENER_NAME, name);
182+
break;
183+
default:
184+
throw new IllegalStateException("Unexpected value: " + type);
185+
}
186+
return command;
187+
}
188+
129189
private static void createShutdownHook() {
130190
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
131191
logger.info("Exporter finishes all activities...");
@@ -142,6 +202,7 @@ private static void createShutdownHook() {
142202
logger.debug("Stopping HTTP server...");
143203
server.stop();
144204
}
205+
MQConnection.close();
145206
logger.info("Goodbye!");
146207
LogManager.shutdown();
147208
}));

0 commit comments

Comments
 (0)