Skip to content

Commit cbfa721

Browse files
committed
Basic support for BytesMessage
1 parent 864220f commit cbfa721

File tree

9 files changed

+282
-16
lines changed

9 files changed

+282
-16
lines changed

bytesmessage_test.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Copyright (c) IBM Corporation 2020
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*/
10+
package main
11+
12+
import (
13+
"testing"
14+
15+
"github.com/ibm-messaging/mq-golang-jms20/jms20subset"
16+
"github.com/ibm-messaging/mq-golang-jms20/mqjms"
17+
"github.com/stretchr/testify/assert"
18+
)
19+
20+
/*
21+
* Test the creation of a bytes message and setting the content.
22+
*/
23+
func TestBytesMessageBody(t *testing.T) {
24+
25+
// Loads CF parameters from connection_info.json and apiKey.json in the Downloads directory
26+
cf, cfErr := mqjms.CreateConnectionFactoryFromDefaultJSONFiles()
27+
assert.Nil(t, cfErr)
28+
29+
// Creates a connection to the queue manager, using defer to close it automatically
30+
// at the end of the function (if it was created successfully)
31+
context, ctxErr := cf.CreateContext()
32+
assert.Nil(t, ctxErr)
33+
if context != nil {
34+
defer context.Close()
35+
}
36+
37+
// Create a BytesMessage and check that we can populate it
38+
msgBody := []byte{'a', 'e', 'i', 'o', 'u'}
39+
msg := context.CreateBytesMessage()
40+
msg.WriteBytes(msgBody)
41+
assert.Equal(t, 5, msg.GetBodyLength())
42+
assert.Equal(t, msgBody, *msg.ReadBytes())
43+
44+
// Create an empty BytesMessage and check that we query it without errors
45+
msg = context.CreateBytesMessage()
46+
assert.Equal(t, 0, msg.GetBodyLength())
47+
assert.Equal(t, []byte{}, *msg.ReadBytes())
48+
49+
}
50+
51+
/*
52+
* Test send and receive of a bytes message with no content.
53+
*/
54+
func TestBytesMessageNilBody(t *testing.T) {
55+
56+
// Loads CF parameters from connection_info.json and apiKey.json in the Downloads directory
57+
cf, cfErr := mqjms.CreateConnectionFactoryFromDefaultJSONFiles()
58+
assert.Nil(t, cfErr)
59+
60+
// Creates a connection to the queue manager, using defer to close it automatically
61+
// at the end of the function (if it was created successfully)
62+
context, ctxErr := cf.CreateContext()
63+
assert.Nil(t, ctxErr)
64+
if context != nil {
65+
defer context.Close()
66+
}
67+
68+
// Create a BytesMessage, and check it has nil content.
69+
msg := context.CreateBytesMessage()
70+
assert.Equal(t, []byte{}, *msg.ReadBytes())
71+
72+
// Now send the message and get it back again, to check that it roundtripped.
73+
queue := context.CreateQueue("DEV.QUEUE.1")
74+
errSend := context.CreateProducer().SetTimeToLive(5000).Send(queue, msg)
75+
assert.Nil(t, errSend)
76+
77+
consumer, errCons := context.CreateConsumer(queue)
78+
if consumer != nil {
79+
defer consumer.Close()
80+
}
81+
assert.Nil(t, errCons)
82+
83+
rcvMsg, errRvc := consumer.ReceiveNoWait()
84+
assert.Nil(t, errRvc)
85+
assert.NotNil(t, rcvMsg)
86+
87+
switch msg2 := rcvMsg.(type) {
88+
case jms20subset.BytesMessage:
89+
assert.Equal(t, 0, msg2.GetBodyLength())
90+
assert.Equal(t, []byte{}, *msg2.ReadBytes())
91+
default:
92+
assert.Fail(t, "Got something other than a text message")
93+
}
94+
95+
}
96+
97+
/*
98+
* Test send and receive of a bytes message with no content.
99+
*/
100+
func TestBytesMessageWithBody(t *testing.T) {
101+
102+
// Loads CF parameters from connection_info.json and apiKey.json in the Downloads directory
103+
cf, cfErr := mqjms.CreateConnectionFactoryFromDefaultJSONFiles()
104+
assert.Nil(t, cfErr)
105+
106+
// Creates a connection to the queue manager, using defer to close it automatically
107+
// at the end of the function (if it was created successfully)
108+
context, ctxErr := cf.CreateContext()
109+
assert.Nil(t, ctxErr)
110+
if context != nil {
111+
defer context.Close()
112+
}
113+
114+
// Create a BytesMessage, and check it has nil content.
115+
msgBody := []byte{'b', 'y', 't', 'e', 's', 'm', 'e', 's', 's', 'a', 'g', 'e'}
116+
msg := context.CreateBytesMessage()
117+
msg.WriteBytes(msgBody)
118+
assert.Equal(t, 12, msg.GetBodyLength())
119+
assert.Equal(t, msgBody, *msg.ReadBytes())
120+
121+
// Now send the message and get it back again, to check that it roundtripped.
122+
queue := context.CreateQueue("DEV.QUEUE.1")
123+
errSend := context.CreateProducer().SetTimeToLive(5000).Send(queue, msg)
124+
assert.Nil(t, errSend)
125+
126+
consumer, errCons := context.CreateConsumer(queue)
127+
if consumer != nil {
128+
defer consumer.Close()
129+
}
130+
assert.Nil(t, errCons)
131+
132+
rcvMsg, errRvc := consumer.ReceiveNoWait()
133+
assert.Nil(t, errRvc)
134+
assert.NotNil(t, rcvMsg)
135+
136+
switch msg2 := rcvMsg.(type) {
137+
case jms20subset.BytesMessage:
138+
assert.Equal(t, len(msgBody), msg2.GetBodyLength())
139+
assert.Equal(t, msgBody, *msg2.ReadBytes())
140+
default:
141+
assert.Fail(t, "Got something other than a text message")
142+
}
143+
144+
}

getbycorrelid_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
package main
1111

1212
import (
13+
"fmt"
14+
"reflect"
15+
"testing"
16+
1317
"github.com/ibm-messaging/mq-golang-jms20/jms20subset"
1418
"github.com/ibm-messaging/mq-golang-jms20/mqjms"
1519
"github.com/stretchr/testify/assert"
16-
"testing"
1720
)
1821

1922
/*
@@ -82,6 +85,7 @@ func TestGetByCorrelID(t *testing.T) {
8285
case jms20subset.TextMessage:
8386
assert.Equal(t, myMsgThreeStr, *msg.GetText())
8487
default:
88+
fmt.Println(reflect.TypeOf(gotCorrelMsg))
8589
assert.Fail(t, "Got something other than a text message")
8690
}
8791

jms20subset/BytesMessage.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Derived from the Eclipse Project for JMS, available at;
2+
// https://github.com/eclipse-ee4j/jms-api
3+
//
4+
// This program and the accompanying materials are made available under the
5+
// terms of the Eclipse Public License 2.0, which is available at
6+
// http://www.eclipse.org/legal/epl-2.0.
7+
//
8+
// SPDX-License-Identifier: EPL-2.0
9+
10+
// Package jms20subset provides interfaces for messaging applications in the style of the Java Message Service (JMS) API.
11+
package jms20subset
12+
13+
// BytesMessage is used to send a message containing a slice of bytes
14+
//
15+
// Instances of this object are created using the functions on the JMSContext
16+
// such as CreateBytesMessage and CreateBytesMessageWithBytes.
17+
type BytesMessage interface {
18+
19+
// Encapsulate the root Message type so that this interface "inherits" the
20+
// accessors for standard attributes that apply to all message types, such
21+
// as GetJMSMessageID.
22+
Message
23+
24+
// ReadBytes returns the bytes contained in this message's data.
25+
ReadBytes() *[]byte
26+
27+
// WriteBytes sets the bytes for this message's data.
28+
WriteBytes(bytes []byte)
29+
30+
GetBodyLength() int
31+
}

jms20subset/JMSContext.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ type JMSContext interface {
6060
// name and different parameters we must use a different function name.
6161
CreateTextMessageWithString(txt string) TextMessage
6262

63+
// CreateBytesMessage creates a message object that is used to send a slice
64+
// of bytes from one application to another.
65+
CreateBytesMessage() BytesMessage
66+
6367
// Commit confirms all messages sent/received during this transaction.
6468
Commit()
6569

mqjms/BytesMessageImpl.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) IBM Corporation 2020.
2+
//
3+
// This program and the accompanying materials are made available under the
4+
// terms of the Eclipse Public License 2.0, which is available at
5+
// http://www.eclipse.org/legal/epl-2.0.
6+
//
7+
// SPDX-License-Identifier: EPL-2.0
8+
9+
// Package mqjms provides the implementation of the JMS style Golang interfaces to communicate with IBM MQ.
10+
package mqjms
11+
12+
// BytesMessageImpl contains the IBM MQ specific attributes necessary to
13+
// present a message that carries a slice of bytes
14+
type BytesMessageImpl struct {
15+
bodyBytes *[]byte
16+
MessageImpl // embed the "parent" message object that defines the basic behaviour
17+
}
18+
19+
// ReadBytes returns the string that is contained in this BytesMessage.
20+
func (msg *BytesMessageImpl) ReadBytes() *[]byte {
21+
22+
if msg.bodyBytes == nil {
23+
return &[]byte{}
24+
}
25+
return msg.bodyBytes
26+
27+
}
28+
29+
// WriteBytes stores the supplied slice of bytes so that it can be transmitted as part
30+
// of this BytesMessage.
31+
func (msg *BytesMessageImpl) WriteBytes(bytes []byte) {
32+
33+
msg.bodyBytes = &bytes
34+
35+
}
36+
37+
// GetBodyLength returns the length of the bytes that are stored in this message
38+
func (msg *BytesMessageImpl) GetBodyLength() int {
39+
40+
length := 0
41+
42+
if msg.bodyBytes != nil {
43+
length = len(*msg.bodyBytes)
44+
}
45+
46+
return length
47+
48+
}

mqjms/ConsumerImpl.go

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,18 +87,35 @@ func (consumer ConsumerImpl) receiveInternal(gmo *ibmmq.MQGMO) (jms20subset.Mess
8787
if err == nil {
8888

8989
// Message received successfully (without error).
90-
// Currently we only support TextMessage, so extract the content of the
91-
// message and populate it into a text string.
92-
var msgBodyStr *string
90+
// Determine on the basis of the format field what sort of message to create.
9391

94-
if datalen > 0 {
95-
strContent := strings.TrimSpace(string(buffer[:datalen]))
96-
msgBodyStr = &strContent
97-
}
92+
if getmqmd.Format == ibmmq.MQFMT_STRING {
93+
94+
var msgBodyStr *string
95+
96+
if datalen > 0 {
97+
strContent := strings.TrimSpace(string(buffer[:datalen]))
98+
msgBodyStr = &strContent
99+
}
100+
101+
msg = &TextMessageImpl{
102+
bodyStr: msgBodyStr,
103+
MessageImpl: MessageImpl{mqmd: getmqmd},
104+
}
105+
106+
} else {
107+
108+
if datalen == 0 {
109+
buffer = []byte{}
110+
}
111+
112+
trimmedBuffer := buffer[0:datalen]
98113

99-
msg = &TextMessageImpl{
100-
bodyStr: msgBodyStr,
101-
MessageImpl: MessageImpl{mqmd: getmqmd},
114+
// Not a string, so fall back to BytesMessage
115+
msg = &BytesMessageImpl{
116+
bodyBytes: &trimmedBuffer,
117+
MessageImpl: MessageImpl{mqmd: getmqmd},
118+
}
102119
}
103120

104121
} else {

mqjms/ContextImpl.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ func (ctx ContextImpl) CreateTextMessageWithString(txt string) jms20subset.TextM
121121
}
122122
}
123123

124+
// CreateBytesMessage is a JMS standard mechanism for creating a BytesMessage.
125+
func (ctx ContextImpl) CreateBytesMessage() jms20subset.BytesMessage {
126+
return &BytesMessageImpl{}
127+
}
128+
124129
// Commit confirms all messages that were sent under this transaction.
125130
func (ctx ContextImpl) Commit() {
126131

mqjms/ProducerImpl.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ func (producer ProducerImpl) Send(dest jms20subset.Destination, msg jms20subset.
8989
var buffer []byte
9090

9191
// We have a "Message" object and can use a switch to safely convert it
92-
// to the sub-types in order to convert it appropriately into an MQ message
93-
// object.
92+
// to the implementation type in order to extract generic MQ message
9493
switch typedMsg := msg.(type) {
9594
case *TextMessageImpl:
9695

@@ -100,20 +99,35 @@ func (producer ProducerImpl) Send(dest jms20subset.Destination, msg jms20subset.
10099
putmqmd = typedMsg.mqmd
101100
}
102101

102+
// Store the Put MQMD so that we can later retrieve "out" fields like MsgId
103+
typedMsg.mqmd = putmqmd
104+
103105
// Set up this MQ message to contain the string from the JMS message.
104-
putmqmd.Format = "MQSTR"
106+
putmqmd.Format = ibmmq.MQFMT_STRING
105107
msgStr := typedMsg.GetText()
106108
if msgStr != nil {
107109
buffer = []byte(*msgStr)
108110
}
109111

112+
case *BytesMessageImpl:
113+
114+
// If the message already has an MQMD then use that (for example it might
115+
// contain ReplyTo information)
116+
if typedMsg.mqmd != nil {
117+
putmqmd = typedMsg.mqmd
118+
}
119+
110120
// Store the Put MQMD so that we can later retrieve "out" fields like MsgId
111121
typedMsg.mqmd = putmqmd
112122

123+
// Set up this MQ message to contain the bytes from the JMS message.
124+
putmqmd.Format = ibmmq.MQFMT_NONE
125+
buffer = *typedMsg.ReadBytes()
126+
113127
default:
114128
// This "should never happen"(!) apart from in situations where we are
115129
// part way through adding support for a new message type to this library.
116-
log.Fatal(jms20subset.CreateJMSException("UnexpectedMessageType", "UnexpectedMessageType", nil))
130+
log.Fatal(jms20subset.CreateJMSException("UnexpectedMessageType", "UnexpectedMessageType-send1", nil))
117131
}
118132

119133
// If the producer has a TTL specified then apply it to the put MQMD so

next-features.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ project!
55

66
Not currently implemented:
77
--------------------------
8-
- BytesMessage, receiveBytesBody
98
- MessageListener
109
- SendToQmgr, ReplyToQmgr
1110
- Topics (pub/sub)

0 commit comments

Comments
 (0)