Skip to content

Commit 864220f

Browse files
committed
Refactor TextMessageImpl into parent MessageImpl
1 parent 99bfb37 commit 864220f

File tree

3 files changed

+264
-248
lines changed

3 files changed

+264
-248
lines changed

mqjms/ConsumerImpl.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ func (consumer ConsumerImpl) receiveInternal(gmo *ibmmq.MQGMO) (jms20subset.Mess
9797
}
9898

9999
msg = &TextMessageImpl{
100-
bodyStr: msgBodyStr,
101-
mqmd: getmqmd,
100+
bodyStr: msgBodyStr,
101+
MessageImpl: MessageImpl{mqmd: getmqmd},
102102
}
103103

104104
} else {

mqjms/MessageImpl.go

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
// Copyright (c) IBM Corporation 2019.
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+
import (
13+
"encoding/hex"
14+
"fmt"
15+
"log"
16+
"strconv"
17+
"strings"
18+
"time"
19+
20+
"github.com/ibm-messaging/mq-golang-jms20/jms20subset"
21+
ibmmq "github.com/ibm-messaging/mq-golang/v5/ibmmq"
22+
)
23+
24+
// MessageImpl contains the IBM MQ specific attributes that are
25+
// common to all types of message.
26+
type MessageImpl struct {
27+
mqmd *ibmmq.MQMD
28+
}
29+
30+
// GetJMSDeliveryMode extracts the persistence setting from this message
31+
// and returns it in the JMS delivery mode format.
32+
func (msg *MessageImpl) GetJMSDeliveryMode() int {
33+
34+
// Retrieve the MQ persistence value from the MQ message descriptor.
35+
mqMsgPersistence := msg.mqmd.Persistence
36+
var jmsPersistence int
37+
38+
// Convert the MQ persistence value to the JMS delivery mode value.
39+
if mqMsgPersistence == ibmmq.MQPER_NOT_PERSISTENT {
40+
jmsPersistence = jms20subset.DeliveryMode_NON_PERSISTENT
41+
} else if mqMsgPersistence == ibmmq.MQPER_PERSISTENT {
42+
jmsPersistence = jms20subset.DeliveryMode_PERSISTENT
43+
} else {
44+
// Give some indication if we received something we didn't expect.
45+
fmt.Println("Unexpected persistence value: " + strconv.Itoa(int(mqMsgPersistence)))
46+
}
47+
48+
return jmsPersistence
49+
}
50+
51+
// GetJMSMessageID extracts the message ID from the native MQ message descriptor.
52+
func (msg *MessageImpl) GetJMSMessageID() string {
53+
msgIDStr := ""
54+
55+
// Extract the MsgId field from the MQ message descriptor if one exists.
56+
// Note that if there is no MQMD then there is no messageID to return.
57+
if msg.mqmd != nil && msg.mqmd.MsgId != nil {
58+
msgIDBytes := msg.mqmd.MsgId
59+
msgIDStr = hex.EncodeToString(msgIDBytes)
60+
}
61+
62+
return msgIDStr
63+
}
64+
65+
// SetJMSReplyTo uses the specified Destination object to configure the reply
66+
// attributes of the native MQ message fields.
67+
func (msg *MessageImpl) SetJMSReplyTo(dest jms20subset.Destination) jms20subset.JMSException {
68+
69+
switch typedDest := dest.(type) {
70+
case QueueImpl:
71+
72+
// Reply information is stored in the MQ message descriptor, so we need to
73+
// add one to this message if it doesn't already exist.
74+
if msg.mqmd == nil {
75+
msg.mqmd = ibmmq.NewMQMD()
76+
}
77+
78+
// Save the queue information into the MQMD so that it can be transmitted.
79+
msg.mqmd.ReplyToQ = typedDest.queueName
80+
81+
default:
82+
// This "should never happen"(!) apart from in situations where we are
83+
// part way through adding support for a new destination type to this library.
84+
log.Fatal(jms20subset.CreateJMSException("UnexpectedDestinationType", "UnexpectedDestinationType", nil))
85+
}
86+
87+
// The option to return an error is not currently used.
88+
return nil
89+
}
90+
91+
// GetJMSReplyTo extracts the native reply information from the MQ message
92+
// and populates it into a Destination object.
93+
func (msg *MessageImpl) GetJMSReplyTo() jms20subset.Destination {
94+
var replyDest jms20subset.Destination
95+
replyDest = nil
96+
97+
// Extract the reply information from the native MQ message descriptor.
98+
// Note that if this message doesn't have an MQMD then there is no reply
99+
// destination.
100+
if msg.mqmd != nil && msg.mqmd.ReplyToQ != "" {
101+
replyQ := strings.TrimSpace(msg.mqmd.ReplyToQ)
102+
103+
// Create the Destination object and populate it to be returned.
104+
replyDest = QueueImpl{
105+
queueName: replyQ,
106+
}
107+
}
108+
109+
return replyDest
110+
}
111+
112+
// SetJMSCorrelationID applies the specified correlation ID string to the native
113+
// MQ message field used for correlation purposes.
114+
func (msg *MessageImpl) SetJMSCorrelationID(correlID string) jms20subset.JMSException {
115+
116+
var retErr jms20subset.JMSException
117+
118+
// correlID could either be plain text "myCorrel" or hex encoded bytes "01020304..."
119+
correlHexBytes := convertStringToMQBytes(correlID)
120+
121+
// The CorrelID is carried in the MQ message descriptor, so if there isn't
122+
// one already associated with this message then we need to create one.
123+
if msg.mqmd == nil {
124+
msg.mqmd = ibmmq.NewMQMD()
125+
}
126+
127+
// Store the bytes form of the correlID
128+
msg.mqmd.CorrelId = correlHexBytes
129+
130+
return retErr
131+
}
132+
133+
// Convert a string which is either plain text or an hex encoded strings of bytes
134+
// into an array of bytes that can be used in MQ message descriptors.
135+
func convertStringToMQBytes(strText string) []byte {
136+
137+
// First try to decode the hex string
138+
correlHexBytes, err := hex.DecodeString(strText)
139+
140+
if err != nil {
141+
// Failed to decode hex string, so assume it is plain text and hex encode it
142+
// into bytes.
143+
correlBytes := []byte(strText)
144+
encodedLen := hex.EncodedLen(len(correlBytes))
145+
if encodedLen < 24 {
146+
encodedLen = 24
147+
}
148+
correlHexBytes = make([]byte, encodedLen)
149+
hex.Encode(correlHexBytes, correlBytes)
150+
}
151+
152+
// Make sure we don't try to store more bytes than MQ is expecting.
153+
if len(correlHexBytes) > 48 {
154+
correlHexBytes = correlHexBytes[0:48]
155+
}
156+
157+
return correlHexBytes
158+
159+
}
160+
161+
// GetJMSCorrelationID retrieves the correl ID from the native MQ message
162+
// descriptor field.
163+
func (msg *MessageImpl) GetJMSCorrelationID() string {
164+
correlID := ""
165+
166+
// Note that if there is no MQMD then there is no correlID stored.
167+
if msg.mqmd != nil && msg.mqmd.CorrelId != nil {
168+
169+
// Get hold of the bytes representation of the correlation ID.
170+
correlIDBytes := msg.mqmd.CorrelId
171+
172+
// We want to be able to give back the same content the application
173+
// originally gave us, which could either be an encoded set of bytes, or
174+
// alternative a plain text string.
175+
// Here we identify any padding zero bytes to trim off so that we can try
176+
// to turn it back into a string.
177+
realLength := len(correlIDBytes)
178+
if realLength > 0 {
179+
for correlIDBytes[realLength-1] == 0 {
180+
realLength--
181+
}
182+
}
183+
184+
// Attempt to decode the content back into a string.
185+
dst := make([]byte, hex.DecodedLen(realLength))
186+
n, err := hex.Decode(dst, correlIDBytes[0:realLength])
187+
188+
if err == nil {
189+
// The decode back to a string was successful so pass back that plain
190+
// text string to the caller.
191+
correlID = string(dst[:n])
192+
193+
} else {
194+
195+
// An error occurred while decoding to a plain text string, so encode
196+
// the bytes that we have into a raw string representation themselves.
197+
correlID = hex.EncodeToString(correlIDBytes)
198+
}
199+
200+
}
201+
202+
return correlID
203+
}
204+
205+
// GetJMSTimestamp retrieves the timestamp at which the message was sent from
206+
// the native MQ message descriptor fields.
207+
func (msg *MessageImpl) GetJMSTimestamp() int64 {
208+
209+
// Details on the format for the MQMD PutDate and PutTime are defined here;
210+
// https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.ref.dev.doc/q097650_.html
211+
// PutDate YYYYMMDD
212+
// PutTime HHMMSSTH (GMT)
213+
214+
timestamp := int64(0)
215+
216+
// Note that if there is no MQMD then there is no stored timestamp.
217+
if msg.mqmd != nil && msg.mqmd.PutDate != "" {
218+
219+
// Extract the year, month and day segments from the PutDate
220+
dateStr := msg.mqmd.PutDate
221+
yearStr := dateStr[0:4]
222+
monthStr := dateStr[4:6]
223+
dayStr := dateStr[6:8]
224+
225+
hourStr := "0"
226+
minStr := "0"
227+
secStr := "0"
228+
millisStr := "0"
229+
230+
// If a PutTime is specified then extract the pieces of that time as well.
231+
if msg.mqmd.PutTime != "" {
232+
timeStr := msg.mqmd.PutTime
233+
hourStr = timeStr[0:2]
234+
minStr = timeStr[2:4]
235+
secStr = timeStr[4:6]
236+
237+
// The MQMD time format only gives hundredths of second, so add an extra
238+
// digit to make millis.
239+
// On average picking "5" will be more accurate than "0" as it is in the
240+
// middle of the possible range of real values.
241+
millisStr = timeStr[6:8] + "5"
242+
}
243+
244+
// Turn the string representations into numeric variables.
245+
yearNum, _ := strconv.Atoi(yearStr)
246+
monthNum, _ := strconv.Atoi(monthStr)
247+
dayNum, _ := strconv.Atoi(dayStr)
248+
hourNum, _ := strconv.Atoi(hourStr)
249+
minNum, _ := strconv.Atoi(minStr)
250+
secNum, _ := strconv.Atoi(secStr)
251+
nanosNum, _ := strconv.Atoi(millisStr + "000000")
252+
253+
// Populate a Date object based on the individual parts, and turn it into a
254+
// milliseconds-since-Epoch format, which is what is returned by this method.
255+
timestampObj := time.Date(yearNum, time.Month(monthNum), dayNum, hourNum, minNum, secNum, nanosNum, time.UTC)
256+
timestamp = timestampObj.UnixNano() / 1000000
257+
}
258+
259+
return timestamp
260+
}

0 commit comments

Comments
 (0)