Skip to content

Commit 5d0d8da

Browse files
committed
Selected header properties part 1 - #51
1 parent 2bfa860 commit 5d0d8da

File tree

6 files changed

+479
-35
lines changed

6 files changed

+479
-35
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,32 @@ your own error handling or logging.
128128
* Set the application name (ApplName) on connections - [applname_test.go](applname_test.go)
129129
* Receive messages over 32kb in size by setting the receive buffer size - [largemessage_test.go](largemessage_test.go)
130130
* Asynchronous put - [asyncput_test.go](asyncput_test.go)
131+
* Special header properties such as JMS_IBM_Format - [specialproperties_test.go](specialproperties_test.go)
131132

132133
As normal with Go, you can run any individual testcase by executing a command such as;
133134
```bash
134135
go test -run TestSampleSendReceiveWithErrorHandling
135136
```
136137

138+
### Special header properties supported
139+
The following special header properties are supported for Get, Put or both as listed below.
140+
141+
```
142+
Field name Example Notes
143+
---------------------------------------------------------------------------------------------------------------------------------------------------------
144+
JMS_IBM_PutDate msg.GetStringProperty("JMS_IBM_PutDate") YYYYMMDD
145+
JMS_IBM_PutTime msg.GetStringProperty("JMS_IBM_PutTime") HHMMSSTH
146+
JMS_IBM_Format msg.GetStringProperty("JMS_IBM_Format") MQSTR
147+
msg.SetStringProperty("JMS_IBM_Format", "MYFMT")
148+
JMS_IBM_MQMD_Format msg.GetStringProperty("JMS_IBM_MQMD_Format") MQSTR
149+
msg.SetStringProperty("JMS_IBM_MQMD_Format", "MYFMT")
150+
JMS_IBM_MQMD_MsgId msg.GetJMSMessageID()
151+
JMS_IBM_MQMD_ApplOriginData msg.GetStringProperty("JMS_IBM_MQMD_ApplOriginData")
152+
JMSExpiration msg.GetJMSExpiration()
153+
JMSXAppID msg.GetStringProperty("JMSXAppID") JMSXAppID / PutApplName is set using ConnectionFactory.ApplName
154+
```
155+
156+
137157

138158
## Getting started
139159

applname_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,10 @@ func TestApplName(t *testing.T) {
5858
assert.Nil(t, errRvc)
5959
assert.NotNil(t, rcvMsg)
6060

61-
messageImpl, ok := rcvMsg.(*mqjms.TextMessageImpl)
62-
assert.True(t, ok)
63-
6461
// Check that the application name was successfully stored in the message
6562
// that we sent.
66-
assert.Equal(t, applName, messageImpl.GetApplName())
63+
gotAppName, err := rcvMsg.GetStringProperty("JMSXAppID")
64+
assert.Nil(t, err)
65+
assert.Equal(t, applName, *gotAppName)
6766

6867
}

jms20subset/Message.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ type Message interface {
2525
// handed off to the provider to be sent.
2626
GetJMSTimestamp() int64
2727

28+
// GetJMSExpiration returns the timestamp at which the message is due to
29+
// expire.
30+
GetJMSExpiration() int64
31+
2832
// SetJMSCorrelationID sets the correlation ID for the message which can be
2933
// used to link on message to another. A typical use is to link a response
3034
// message with its request message.

mqjms/MessageImpl.go

Lines changed: 142 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -263,20 +263,18 @@ func (msg *MessageImpl) GetJMSTimestamp() int64 {
263263
return timestamp
264264
}
265265

266-
// GetApplName retrieves the PutApplName field from the MQMD.
267-
// This method is not exposed on the JMS style interface and is mainly for testing purposes.
268-
func (msg *MessageImpl) GetApplName() string {
269-
applName := ""
266+
// GetJMSExpiration returns the timestamp at which the message is due to
267+
// expire.
268+
func (msg *MessageImpl) GetJMSExpiration() int64 {
270269

271-
// Note that if there is no MQMD then there is no correlID stored.
272-
if msg.mqmd != nil {
273-
274-
// Get hold of the bytes representation of the correlation ID.
275-
applName = msg.mqmd.PutApplName
270+
// mqmd.Expiry gives tenths of a second after which message should expire
271+
timestamp := msg.GetJMSTimestamp()
276272

273+
if timestamp != 0 && msg.mqmd.Expiry != 0 {
274+
timestamp += (int64(msg.mqmd.Expiry) * 100)
277275
}
278276

279-
return applName
277+
return timestamp
280278
}
281279

282280
// SetStringProperty enables an application to set a string-type message property.
@@ -288,6 +286,12 @@ func (msg *MessageImpl) SetStringProperty(name string, value *string) jms20subse
288286

289287
var linkedErr error
290288

289+
// Different code path and shortcut for special header properties
290+
isSpecial, _ := msg.setSpecialStringPropertyValue(name, value)
291+
if isSpecial {
292+
return nil
293+
}
294+
291295
if value != nil {
292296
// Looking to set a value
293297
var valueStr string
@@ -314,6 +318,104 @@ func (msg *MessageImpl) SetStringProperty(name string, value *string) jms20subse
314318
return retErr
315319
}
316320

321+
// getSpecialPropertyValue returns the value of special header properties such as
322+
// values from the MQMD that are mapped to JMS properties.
323+
func (msg *MessageImpl) setSpecialStringPropertyValue(name string, value *string) (bool, error) {
324+
325+
// Special properties always start with a known prefix.
326+
if !strings.HasPrefix(name, "JMS") {
327+
return false, nil
328+
}
329+
330+
// Check first that there is an MQMD to write to
331+
if msg.mqmd == nil {
332+
msg.mqmd = ibmmq.NewMQMD()
333+
}
334+
335+
// Assume for now that this property is special as it has passed the basic
336+
// checks, and this value will be set back to false if it doesn't match any
337+
// of the specific fields.
338+
isSpecial := true
339+
340+
var err error
341+
342+
switch name {
343+
case "JMS_IBM_Format":
344+
if value != nil {
345+
msg.mqmd.Format = *value
346+
} else {
347+
msg.mqmd.Format = ibmmq.MQFMT_NONE // unset
348+
}
349+
350+
case "JMS_IBM_MQMD_Format":
351+
if value != nil {
352+
msg.mqmd.Format = *value
353+
} else {
354+
msg.mqmd.Format = ibmmq.MQFMT_NONE // unset
355+
}
356+
357+
default:
358+
isSpecial = false
359+
}
360+
361+
return isSpecial, err
362+
}
363+
364+
// getSpecialPropertyValue returns the value of special header properties such as
365+
// values from the MQMD that are mapped to JMS properties.
366+
func (msg *MessageImpl) getSpecialPropertyValue(name string) (bool, interface{}, error) {
367+
368+
// Special properties always start with a known prefix.
369+
if !strings.HasPrefix(name, "JMS") {
370+
return false, nil, nil
371+
}
372+
373+
// Assume for now that this property is special as it has passed the basic
374+
// checks, and this value will be set back to false if it doesn't match any
375+
// of the specific fields.
376+
isSpecial := true
377+
378+
var value interface{}
379+
var err error
380+
381+
switch name {
382+
case "JMS_IBM_PutDate":
383+
if msg.mqmd != nil {
384+
value = msg.mqmd.PutDate
385+
}
386+
387+
case "JMS_IBM_PutTime":
388+
if msg.mqmd != nil {
389+
value = msg.mqmd.PutTime
390+
}
391+
392+
case "JMS_IBM_Format":
393+
if msg.mqmd != nil && msg.mqmd.Format != ibmmq.MQFMT_NONE {
394+
value = msg.mqmd.Format
395+
}
396+
397+
case "JMS_IBM_MQMD_Format": // same as JMS_IBM_Format
398+
if msg.mqmd != nil && msg.mqmd.Format != ibmmq.MQFMT_NONE {
399+
value = msg.mqmd.Format
400+
}
401+
402+
case "JMSXAppID":
403+
if msg.mqmd != nil {
404+
value = msg.mqmd.PutApplName
405+
}
406+
407+
case "JMS_IBM_MQMD_ApplOriginData":
408+
if msg.mqmd != nil && msg.mqmd.ApplOriginData != ibmmq.MQFMT_NONE {
409+
value = msg.mqmd.ApplOriginData
410+
}
411+
412+
default:
413+
isSpecial = false
414+
}
415+
416+
return isSpecial, value, err
417+
}
418+
317419
// GetStringProperty returns the string value of a named message property.
318420
// Returns nil if the named property is not set.
319421
func (msg *MessageImpl) GetStringProperty(name string) (*string, jms20subset.JMSException) {
@@ -324,32 +426,43 @@ func (msg *MessageImpl) GetStringProperty(name string) (*string, jms20subset.JMS
324426
impo := ibmmq.NewMQIMPO()
325427
pd := ibmmq.NewMQPD()
326428

327-
_, value, err := msg.msgHandle.InqMP(impo, pd, name)
429+
// Check first if this is a special property
430+
isSpecialProp, value, err := msg.getSpecialPropertyValue(name)
431+
432+
if !isSpecialProp {
433+
// If not then look for a user property
434+
_, value, err = msg.msgHandle.InqMP(impo, pd, name)
435+
}
328436

329437
if err == nil {
330438

331439
var parseErr error
332440

333-
switch valueTyped := value.(type) {
334-
case string:
335-
valueStrPtr = &valueTyped
336-
case int64:
337-
valueStr := strconv.FormatInt(valueTyped, 10)
338-
valueStrPtr = &valueStr
339-
if parseErr != nil {
340-
retErr = jms20subset.CreateJMSException(MessageImpl_PROPERTY_CONVERT_FAILED_REASON,
341-
MessageImpl_PROPERTY_CONVERT_FAILED_CODE, parseErr)
441+
if value != nil {
442+
443+
switch valueTyped := value.(type) {
444+
case string:
445+
valueStrPtr = &valueTyped
446+
case int64:
447+
valueStr := strconv.FormatInt(valueTyped, 10)
448+
valueStrPtr = &valueStr
449+
if parseErr != nil {
450+
retErr = jms20subset.CreateJMSException(MessageImpl_PROPERTY_CONVERT_FAILED_REASON,
451+
MessageImpl_PROPERTY_CONVERT_FAILED_CODE, parseErr)
452+
}
453+
case bool:
454+
valueStr := strconv.FormatBool(valueTyped)
455+
valueStrPtr = &valueStr
456+
case float64:
457+
valueStr := fmt.Sprintf("%g", valueTyped)
458+
valueStrPtr = &valueStr
459+
default:
460+
retErr = jms20subset.CreateJMSException(MessageImpl_PROPERTY_CONVERT_NOTSUPPORTED_REASON,
461+
MessageImpl_PROPERTY_CONVERT_NOTSUPPORTED_CODE, parseErr)
342462
}
343-
case bool:
344-
valueStr := strconv.FormatBool(valueTyped)
345-
valueStrPtr = &valueStr
346-
case float64:
347-
valueStr := fmt.Sprintf("%g", valueTyped)
348-
valueStrPtr = &valueStr
349-
default:
350-
retErr = jms20subset.CreateJMSException(MessageImpl_PROPERTY_CONVERT_NOTSUPPORTED_REASON,
351-
MessageImpl_PROPERTY_CONVERT_NOTSUPPORTED_CODE, parseErr)
463+
352464
}
465+
353466
} else {
354467

355468
mqret := err.(*ibmmq.MQReturn)

mqjms/ProducerImpl.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"fmt"
1414
"log"
1515
"strconv"
16+
"strings"
1617

1718
"github.com/ibm-messaging/mq-golang-jms20/jms20subset"
1819
ibmmq "github.com/ibm-messaging/mq-golang/v5/ibmmq"
@@ -110,7 +111,11 @@ func (producer ProducerImpl) Send(dest jms20subset.Destination, msg jms20subset.
110111
typedMsg.mqmd = putmqmd
111112

112113
// Set up this MQ message to contain the string from the JMS message.
113-
putmqmd.Format = ibmmq.MQFMT_STRING
114+
trimmedFormat := strings.TrimSpace(putmqmd.Format)
115+
if trimmedFormat == ibmmq.MQFMT_NONE {
116+
putmqmd.Format = ibmmq.MQFMT_STRING
117+
}
118+
114119
msgStr := typedMsg.GetText()
115120
if msgStr != nil {
116121
buffer = []byte(*msgStr)
@@ -131,7 +136,6 @@ func (producer ProducerImpl) Send(dest jms20subset.Destination, msg jms20subset.
131136
typedMsg.mqmd = putmqmd
132137

133138
// Set up this MQ message to contain the bytes from the JMS message.
134-
putmqmd.Format = ibmmq.MQFMT_NONE
135139
buffer = *typedMsg.ReadBytes()
136140

137141
default:

0 commit comments

Comments
 (0)