Skip to content

Commit

Permalink
Add Dead Letter Header parser
Browse files Browse the repository at this point in the history
  • Loading branch information
ibmmqmet committed Dec 10, 2018
1 parent 5adb005 commit a887947
Show file tree
Hide file tree
Showing 7 changed files with 410 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Samples updated to use "defer" instead of just suggesting it
* Add support for MQCB/MQCTL callback functions
* Add support for MQBEGIN transaction management
* Add Dead Letter Header parser

## November 2018 - v3.2.0
* Added GetPlatform to mqmetric so it can be used as a label/tag in collectors
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,6 @@ At this point, you should have a compiled copy of the program in `$GOPATH/bin`.

All regular MQI verbs are now available through the `ibmmq` package.

There are no structure handlers for message headers such as MQRFH2 or MQDLH.

## History

See [CHANGELOG](CHANGELOG.md) in this directory.
Expand Down
34 changes: 32 additions & 2 deletions ibmmq/mqi.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import "C"

import (
"encoding/binary"
"io"
"strings"
"unsafe"
)
Expand Down Expand Up @@ -120,6 +121,8 @@ func (e *MQReturn) Error() string {
return mqstrerror(e.verb, C.MQLONG(e.MQCC), C.MQLONG(e.MQRC))
}

var endian binary.ByteOrder // Used by structure formatters such as MQCFH

/*
* Copy a Go string in "strings"
* to a fixed-size C char array such as MQCHAR12
Expand All @@ -139,7 +142,7 @@ func setMQIString(a *C.char, v string, l int) {
/*
* The C.GoStringN function can return strings that include
* NUL characters (which is not really what is expected for a C string-related
* function). So we have a utility function to remove any trailing nulls
* function). So we have a utility function to remove any trailing nulls and spaces
*/
func trimStringN(c *C.char, l C.int) string {
var rc string
Expand All @@ -150,7 +153,7 @@ func trimStringN(c *C.char, l C.int) string {
} else {
rc = s[0:i]
}
return rc
return strings.TrimSpace(rc)
}

/*
Expand Down Expand Up @@ -1261,3 +1264,30 @@ func (handle *MQMessageHandle) InqMP(goimpo *MQIMPO, gopd *MQPD, name string) (s

return goimpo.ReturnedName, propertyValue, nil
}

/*
GetHeader returns a structure containing a parsed-out version of an MQI
message header such as the MQDLH (which is currently the only structure
supported). Other structures like the RFH2 could follow.
The caller of this function needs to cast the returned structure to the
specific type in order to reference the fields.
*/
func GetHeader(md *MQMD, buf []byte) (interface{}, int, error) {
switch md.Format {
case MQFMT_DEAD_LETTER_HEADER:
return getHeaderDLH(md, buf)
}

mqreturn := &MQReturn{MQCC: int32(MQCC_FAILED),
MQRC: int32(MQRC_FORMAT_NOT_SUPPORTED),
}

return nil, 0, mqreturn
}

func readStringFromFixedBuffer(r io.Reader, l int32) string {
tmpBuf := make([]byte, l)
binary.Read(r, endian, tmpBuf)
return strings.TrimSpace(string(tmpBuf))
}
148 changes: 148 additions & 0 deletions ibmmq/mqiDLH.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package ibmmq

/*
Copyright (c) IBM Corporation 2016,2018
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributors:
Mark Taylor - Initial Contribution
*/

/*
#include <stdlib.h>
#include <cmqc.h>
#include <cmqcfc.h>
*/
import "C"

import (
"bytes"
"encoding/binary"
)

type MQDLH struct {
Reason int32
DestQName string
DestQMgrName string
Encoding int32
CodedCharSetId int32
Format string
PutApplType int32
PutApplName string
PutDate string
PutTime string
strucLength int // Not exported
}

func NewMQDLH(md *MQMD) *MQDLH {
dlh := new(MQDLH)
dlh.Reason = MQRC_NONE
dlh.CodedCharSetId = MQCCSI_UNDEFINED
dlh.PutApplType = 0
dlh.PutApplName = ""
dlh.PutTime = ""
dlh.PutDate = ""
dlh.Format = ""
dlh.DestQName = ""
dlh.DestQMgrName = ""

dlh.strucLength = int(MQDLH_CURRENT_LENGTH)

if md != nil {
dlh.Encoding = md.Encoding
if md.CodedCharSetId == MQCCSI_DEFAULT {
dlh.CodedCharSetId = MQCCSI_INHERIT
} else {
dlh.CodedCharSetId = md.CodedCharSetId
}
dlh.Format = md.Format

md.Format = MQFMT_DEAD_LETTER_HEADER
md.MsgType = MQMT_REPORT
md.CodedCharSetId = MQCCSI_Q_MGR
}

if (C.MQENC_NATIVE % 2) == 0 {
endian = binary.LittleEndian
} else {
endian = binary.BigEndian
}

return dlh
}

func (dlh *MQDLH) Bytes() []byte {
buf := make([]byte, dlh.strucLength)
offset := 0

copy(buf[offset:], "DLH ")
offset += 4
endian.PutUint32(buf[offset:], uint32(MQDLH_CURRENT_VERSION))
offset += 4
endian.PutUint32(buf[offset:], uint32(dlh.Reason))
offset += 4
copy(buf[offset:], dlh.DestQName)
offset += int(MQ_OBJECT_NAME_LENGTH)
copy(buf[offset:], dlh.DestQMgrName)
offset += int(MQ_Q_MGR_NAME_LENGTH)
endian.PutUint32(buf[offset:], uint32(dlh.Encoding))
offset += 4
endian.PutUint32(buf[offset:], uint32(dlh.CodedCharSetId))
offset += 4
copy(buf[offset:], dlh.Format)
offset += int(MQ_FORMAT_LENGTH)
endian.PutUint32(buf[offset:], uint32(dlh.PutApplType))
offset += 4
copy(buf[offset:], dlh.PutApplName)
offset += int(MQ_PUT_APPL_NAME_LENGTH)
copy(buf[offset:], dlh.PutDate)
offset += int(MQ_PUT_DATE_LENGTH)
copy(buf[offset:], dlh.PutTime)
offset += int(MQ_PUT_TIME_LENGTH)

return buf
}

/*
We have a byte array for the message contents. The start of that buffer
is the MQDLH structure. We read the bytes from that fixed header to match
the C structure definition for each field. The DLH does not have multiple
versions defined so we don't need to check that as we go through.
*/
func getHeaderDLH(md *MQMD, buf []byte) (*MQDLH, int, error) {

var version int32

dlh := NewMQDLH(nil)

r := bytes.NewBuffer(buf)
_ = readStringFromFixedBuffer(r, 4) // StrucId
binary.Read(r, endian, &version)
binary.Read(r, endian, &dlh.Reason)
dlh.DestQName = readStringFromFixedBuffer(r, MQ_OBJECT_NAME_LENGTH)
dlh.DestQMgrName = readStringFromFixedBuffer(r, MQ_Q_MGR_NAME_LENGTH)

binary.Read(r, endian, &dlh.Encoding)
binary.Read(r, endian, &dlh.CodedCharSetId)

dlh.Format = readStringFromFixedBuffer(r, MQ_FORMAT_LENGTH)

binary.Read(r, endian, &dlh.PutApplType)

dlh.PutApplName = readStringFromFixedBuffer(r, MQ_PUT_APPL_NAME_LENGTH)
dlh.PutDate = readStringFromFixedBuffer(r, MQ_PUT_DATE_LENGTH)
dlh.PutTime = readStringFromFixedBuffer(r, MQ_PUT_TIME_LENGTH)

return dlh, dlh.strucLength, nil
}
2 changes: 0 additions & 2 deletions ibmmq/mqiPCF.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ type MQCFH struct {
ParameterCount int32
}

var endian binary.ByteOrder

/*
PCFParameter is a structure containing the data associated with
various types of PCF element. Use the Type field to decide which
Expand Down
2 changes: 1 addition & 1 deletion mqmetric/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ func formatDescription(elem *MonElement) string {
// we have to ensure uniqueness.
if strings.Contains(elem.Description, "byte count") {
s = s + "_bytes"
} else if strings.HasSuffix(elem.Description," count") && !strings.Contains(s,"_count") {
} else if strings.HasSuffix(elem.Description, " count") && !strings.Contains(s, "_count") {
s = s + "_count"
}
}
Expand Down
Loading

0 comments on commit a887947

Please sign in to comment.