Skip to content

Commit 8a4a078

Browse files
committed
ibm MessageQueue reader and writer
1 parent 8868f6f commit 8a4a078

File tree

5 files changed

+405
-0
lines changed

5 files changed

+405
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# MQ connection properties (case-insensitive keys)
2+
HOST=
3+
PORT=
4+
CHANNEL=
5+
QMGR=
6+
QUEUE=
7+
TRUSTSTORE_PATH=
8+
TRUSTSTORE_TYPE=
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>app</groupId>
7+
<artifactId>mq-reader</artifactId>
8+
<version>1.0.0</version>
9+
10+
<properties>
11+
<maven.compiler.source>21</maven.compiler.source>
12+
<maven.compiler.target>21</maven.compiler.target>
13+
</properties>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>com.ibm.mq</groupId>
18+
<artifactId>com.ibm.mq.allclient</artifactId>
19+
<version>9.3.5.0</version>
20+
</dependency>
21+
</dependencies>
22+
</project>
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package app;
2+
3+
import com.ibm.mq.constants.CMQC;
4+
5+
import java.io.IOException;
6+
import java.io.InputStream;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import java.util.Hashtable;
10+
import java.util.Properties;
11+
12+
final class MQCommon {
13+
private static final String CIPHER_SUITE = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
14+
15+
private static final String ENV_USER = "MQ_USER";
16+
private static final String ENV_PASSWORD = "MQ_PASSWORD";
17+
private static final String ENV_TRUSTSTORE_PASSWORD = "TRUSTSTORE_PASSWORD";
18+
19+
private MQCommon() {
20+
}
21+
22+
static Config loadConfig(Path propertiesPath) throws IOException {
23+
Properties properties = new Properties();
24+
try (InputStream input = Files.newInputStream(propertiesPath)) {
25+
properties.load(input);
26+
}
27+
28+
String host = getRequiredProperty(properties, "HOST");
29+
int port = Integer.parseInt(getRequiredProperty(properties, "PORT"));
30+
String channel = getRequiredProperty(properties, "CHANNEL");
31+
String qmgr = getRequiredProperty(properties, "QMGR");
32+
String queue = getRequiredProperty(properties, "QUEUE");
33+
String truststorePath = getRequiredProperty(properties, "TRUSTSTORE_PATH");
34+
String truststoreType = getRequiredProperty(properties, "TRUSTSTORE_TYPE");
35+
36+
String user = getRequiredEnv(ENV_USER);
37+
String password = getRequiredEnv(ENV_PASSWORD);
38+
String truststorePassword = getRequiredEnv(ENV_TRUSTSTORE_PASSWORD);
39+
40+
return new Config(
41+
host,
42+
port,
43+
channel,
44+
qmgr,
45+
queue,
46+
user,
47+
password,
48+
truststorePath,
49+
truststoreType,
50+
truststorePassword
51+
);
52+
}
53+
54+
static Hashtable<String, Object> createMqProps(Config config) {
55+
Hashtable<String, Object> props = new Hashtable<>();
56+
props.put(CMQC.HOST_NAME_PROPERTY, config.host);
57+
props.put(CMQC.PORT_PROPERTY, config.port);
58+
props.put(CMQC.CHANNEL_PROPERTY, config.channel);
59+
props.put(CMQC.USER_ID_PROPERTY, config.user);
60+
props.put(CMQC.PASSWORD_PROPERTY, config.password);
61+
props.put(CMQC.TRANSPORT_PROPERTY, CMQC.TRANSPORT_MQSERIES_CLIENT);
62+
props.put(CMQC.SSL_CIPHER_SUITE_PROPERTY, CIPHER_SUITE);
63+
return props;
64+
}
65+
66+
static void configureSsl(Config config) {
67+
System.setProperty("com.ibm.mq.cfg.useIBMCipherMappings", "false");
68+
System.setProperty("com.ibm.mq.client.useAuthentication", "true");
69+
System.setProperty("javax.net.ssl.trustStore", config.truststorePath);
70+
System.setProperty("javax.net.ssl.trustStorePassword", config.truststorePassword);
71+
System.setProperty("javax.net.ssl.trustStoreType", config.truststoreType);
72+
}
73+
74+
private static String getRequiredProperty(Properties properties, String key) {
75+
String value = firstNonBlank(
76+
properties.getProperty(key),
77+
properties.getProperty(key.toLowerCase()),
78+
properties.getProperty(key.toUpperCase())
79+
);
80+
if (value == null) {
81+
throw new IllegalArgumentException("Missing property: " + key);
82+
}
83+
return value.trim();
84+
}
85+
86+
private static String getRequiredEnv(String name) {
87+
String value = System.getenv(name);
88+
if (value == null || value.isBlank()) {
89+
throw new IllegalStateException("Missing environment variable: " + name);
90+
}
91+
return value;
92+
}
93+
94+
private static String firstNonBlank(String... values) {
95+
for (String value : values) {
96+
if (value != null && !value.isBlank()) {
97+
return value;
98+
}
99+
}
100+
return null;
101+
}
102+
103+
static final class Config {
104+
final String host;
105+
final int port;
106+
final String channel;
107+
final String qmgr;
108+
final String queue;
109+
final String user;
110+
final String password;
111+
final String truststorePath;
112+
final String truststoreType;
113+
final String truststorePassword;
114+
115+
Config(
116+
String host,
117+
int port,
118+
String channel,
119+
String qmgr,
120+
String queue,
121+
String user,
122+
String password,
123+
String truststorePath,
124+
String truststoreType,
125+
String truststorePassword
126+
) {
127+
this.host = host;
128+
this.port = port;
129+
this.channel = channel;
130+
this.qmgr = qmgr;
131+
this.queue = queue;
132+
this.user = user;
133+
this.password = password;
134+
this.truststorePath = truststorePath;
135+
this.truststoreType = truststoreType;
136+
this.truststorePassword = truststorePassword;
137+
}
138+
}
139+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
Required env vars:
3+
4+
export MQ_USER=''
5+
export MQ_PASSWORD=''
6+
export TRUSTSTORE_PASSWORD=""
7+
8+
# Read-only first 25
9+
mvn -q -DskipTests exec:java -Dexec.mainClass=app.MQReader -Dexec.args="config/mq.properties 25 read_only"
10+
11+
# Read-only all
12+
mvn -q -DskipTests exec:java -Dexec.mainClass=app.MQReader -Dexec.args="config/mq.properties 0 read_only"
13+
14+
# Read and remove (default)
15+
mvn -q -DskipTests exec:java -Dexec.mainClass=app.MQReader -Dexec.args="config/mq.properties 25 read_and_remove"
16+
17+
# Read and remove all
18+
mvn -q -DskipTests exec:java -Dexec.mainClass=app.MQReader -Dexec.args="config/mq.properties 0 read_and_remove"
19+
*/
20+
21+
package app;
22+
23+
import com.ibm.mq.MQException;
24+
import com.ibm.mq.MQGetMessageOptions;
25+
import com.ibm.mq.MQMessage;
26+
import com.ibm.mq.MQQueue;
27+
import com.ibm.mq.MQQueueManager;
28+
import com.ibm.mq.constants.CMQC;
29+
30+
import java.nio.file.Path;
31+
32+
public class MQReader {
33+
private static final int DEFAULT_LIMIT = 10;
34+
35+
public static void main(String[] args) throws Exception {
36+
if (args.length < 1) {
37+
printUsageAndExit();
38+
}
39+
40+
Path propertiesPath = Path.of(args[0]);
41+
int limit = DEFAULT_LIMIT;
42+
boolean readOnly = false;
43+
if (args.length > 1) {
44+
limit = Integer.parseInt(args[1]);
45+
}
46+
if (args.length > 2) {
47+
String mode = args[2].toLowerCase();
48+
if ("read_only".equals(mode)) {
49+
readOnly = true;
50+
} else if ("read_and_remove".equals(mode)) {
51+
readOnly = false;
52+
} else {
53+
throw new IllegalArgumentException("Unknown mode: " + args[2] + " (use read_only or read_and_remove)");
54+
}
55+
}
56+
57+
MQCommon.Config config = MQCommon.loadConfig(propertiesPath);
58+
MQCommon.configureSsl(config);
59+
java.util.Hashtable<String, Object> props = MQCommon.createMqProps(config);
60+
61+
MQQueueManager qmgr = null;
62+
MQQueue queue = null;
63+
64+
try {
65+
qmgr = new MQQueueManager(config.qmgr, props);
66+
int openOptions = (readOnly ? CMQC.MQOO_BROWSE : CMQC.MQOO_INPUT_AS_Q_DEF) | CMQC.MQOO_FAIL_IF_QUIESCING;
67+
queue = qmgr.accessQueue(config.queue, openOptions);
68+
69+
int count = 0;
70+
boolean first = true;
71+
while (limit == 0 || count < limit) {
72+
MQMessage message = new MQMessage();
73+
MQGetMessageOptions gmo = new MQGetMessageOptions();
74+
if (readOnly) {
75+
gmo.options = CMQC.MQGMO_NO_WAIT | CMQC.MQGMO_FAIL_IF_QUIESCING
76+
| (first ? CMQC.MQGMO_BROWSE_FIRST : CMQC.MQGMO_BROWSE_NEXT);
77+
} else {
78+
gmo.options = CMQC.MQGMO_NO_WAIT | CMQC.MQGMO_FAIL_IF_QUIESCING;
79+
}
80+
81+
try {
82+
queue.get(message, gmo);
83+
String body = message.readStringOfByteLength(message.getDataLength());
84+
System.out.println(body);
85+
count++;
86+
first = false;
87+
} catch (MQException e) {
88+
if (e.reasonCode == CMQC.MQRC_NO_MSG_AVAILABLE) {
89+
break;
90+
}
91+
throw e;
92+
}
93+
}
94+
System.out.println("Read " + count + " messages.");
95+
} finally {
96+
if (queue != null) {
97+
try {
98+
queue.close();
99+
} catch (MQException ignored) {
100+
// Best-effort cleanup.
101+
}
102+
}
103+
if (qmgr != null) {
104+
try {
105+
qmgr.disconnect();
106+
} catch (MQException ignored) {
107+
// Best-effort cleanup.
108+
}
109+
}
110+
}
111+
}
112+
113+
private static void printUsageAndExit() {
114+
System.out.println("Usage:");
115+
System.out.println(" mvn -q -DskipTests exec:java -Dexec.mainClass=app.MQReader -Dexec.args=\"/path/to/mq.properties 25 read_and_remove\"");
116+
System.out.println(" mvn -q -DskipTests exec:java -Dexec.mainClass=app.MQReader -Dexec.args=\"/path/to/mq.properties 25 read_only\"");
117+
System.out.println(" mvn -q -DskipTests exec:java -Dexec.mainClass=app.MQReader -Dexec.args=\"/path/to/mq.properties 0\"");
118+
System.out.println("Env vars: MQ_USER, MQ_PASSWORD, TRUSTSTORE_PASSWORD");
119+
System.exit(1);
120+
}
121+
}

0 commit comments

Comments
 (0)