Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Callback #70

Merged
merged 7 commits into from
Dec 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## December 2018 - v3.3.0
* All relevant API calls now automatically set FAIL_IF_QUIESCING
* 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
* Added sample programs demonstrating specific operations such as put/get of message
Expand Down
7 changes: 1 addition & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,7 @@ At this point, you should have a compiled copy of the program in `$GOPATH/bin`.

## Limitations

Almost all of the MQI verbs are now available through the `ibmmq` package.
Currently unavailable verbs include:

* MQCB/MQCTL

There are also no structure handlers for message headers such as MQRFH2 or MQDLH.
All regular MQI verbs are now available through the `ibmmq` package.

## History

Expand Down
75 changes: 70 additions & 5 deletions ibmmq/mqi.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,13 @@ package ibmmq
#include <string.h>
#include <cmqc.h>
#include <cmqxc.h>

*/
import "C"

import (
"encoding/binary"
"io"
"strings"
"unsafe"
)
Expand Down Expand Up @@ -119,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 @@ -138,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 @@ -149,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 @@ -219,6 +223,7 @@ func (x *MQQueueManager) Disc() error {
var mqrc C.MQLONG
var mqcc C.MQLONG

savedConn := x.hConn
C.MQDISC(&x.hConn, &mqcc, &mqrc)

mqreturn := MQReturn{MQCC: int32(mqcc),
Expand All @@ -230,6 +235,8 @@ func (x *MQQueueManager) Disc() error {
return &mqreturn
}

cbRemoveConnection(savedConn)

return nil
}

Expand All @@ -248,7 +255,7 @@ func (x *MQQueueManager) Open(good *MQOD, goOpenOptions int32) (MQObject, error)
}

copyODtoC(&mqod, good)
mqOpenOptions = C.MQLONG(goOpenOptions)
mqOpenOptions = C.MQLONG(goOpenOptions) | C.MQOO_FAIL_IF_QUIESCING
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we always setting MQOO_FAIL_IF_QUIESCING here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FAIL_IF_QUIESCING should always be set - it's one of the big complaints about it not being a default. There's no good reason not to use it in applications. So forcing it here makes user applications - and the sample programs - simpler as they don't need to think about it (assuming they ever did, and then wondered why qmgrs didn't end cleanly or restart). I did similar in the Node.js implementation based on user feedback.

Yes, the code could return "key" directly but setting a variable made it easier to insert Printfs during debug.


C.MQOPEN(x.hConn,
(C.PMQVOID)(unsafe.Pointer(&mqod)),
Expand Down Expand Up @@ -288,6 +295,9 @@ func (object *MQObject) Close(goCloseOptions int32) error {

mqCloseOptions = C.MQLONG(goCloseOptions)

savedHConn := object.qMgr.hConn
savedHObj := object.hObj

C.MQCLOSE(object.qMgr.hConn, &object.hObj, mqCloseOptions, &mqcc, &mqrc)

mqreturn := MQReturn{MQCC: int32(mqcc),
Expand All @@ -299,6 +309,7 @@ func (object *MQObject) Close(goCloseOptions int32) error {
return &mqreturn
}

cbRemoveHandle(savedHConn, savedHObj)
return nil

}
Expand Down Expand Up @@ -373,6 +384,33 @@ func (subObject *MQObject) Subrq(gosro *MQSRO, action int32) error {
return nil
}

/*
Begin is the function to start a two-phase XA transaction coordinated by MQ
*/
func (x *MQQueueManager) Begin(gobo *MQBO) error {
var mqrc C.MQLONG
var mqcc C.MQLONG
var mqbo C.MQBO

copyBOtoC(&mqbo, gobo)

C.MQBEGIN(x.hConn, (C.PMQVOID)(unsafe.Pointer(&mqbo)), &mqcc, &mqrc)

copyBOfromC(&mqbo, gobo)

mqreturn := MQReturn{MQCC: int32(mqcc),
MQRC: int32(mqrc),
verb: "MQBEGIN",
}

if mqcc != C.MQCC_OK {
return &mqreturn
}

return nil

}

/*
Cmit is the function to commit an in-flight transaction
*/
Expand Down Expand Up @@ -1214,15 +1252,42 @@ func (handle *MQMessageHandle) InqMP(goimpo *MQIMPO, gopd *MQPD, name string) (s
propertyValue = true
}
case C.MQTYPE_STRING:
propertyValue = C.GoStringN((*C.char)(propertyPtr), propertyLength)
propertyValue = C.GoStringN((*C.char)(propertyPtr), (C.int)(propertyLength))
case C.MQTYPE_BYTE_STRING:
ba := make([]byte, propertyLength)
p := (*C.MQBYTE)(propertyPtr)
copy(ba[:], C.GoBytes(unsafe.Pointer(p), propertyLength))
copy(ba[:], C.GoBytes(unsafe.Pointer(p), (C.int)(propertyLength)))
propertyValue = ba
case C.MQTYPE_NULL:
propertyValue = nil
}

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))
}
54 changes: 54 additions & 0 deletions ibmmq/mqiBO.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package ibmmq

/*
Copyright (c) IBM Corporation 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 <string.h>
#include <cmqc.h>

*/
import "C"

/*
This module contains the Begin Options structure
*/

type MQBO struct {
Options int32
}

func NewMQBO() *MQBO {
bo := new(MQBO)
bo.Options = int32(C.MQBO_NONE)
return bo
}

func copyBOtoC(mqbo *C.MQBO, gobo *MQBO) {
setMQIString((*C.char)(&mqbo.StrucId[0]), "BO ", 4)
mqbo.Version = 1
mqbo.Options = C.MQLONG(gobo.Options)
}

func copyBOfromC(mqbo *C.MQBO, gobo *MQBO) {
gobo.Options = int32(mqbo.Options)
return
}
78 changes: 78 additions & 0 deletions ibmmq/mqiCBC.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package ibmmq

/*
Copyright (c) IBM Corporation 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 <string.h>
#include <cmqc.h>

*/
import "C"

/*
MQCBC is a structure containing the MQ Callback Context
The CompCode and Reason in the C structure are not included here. They
are set in an independent MQReturn structure passed to the callback. Similarly
for the hObj
*/
type MQCBC struct {
CallType int32
CallbackArea interface{} // These fields are saved/restored in parent function
ConnectionArea interface{}
State int32
DataLength int32
BufferLength int32
Flags int32
ReconnectDelay int32
}

/*
NewMQCBC creates a MQCBC structure. There are no default values
as the structure is created within MQ.
*/
func NewMQCBC() *MQCBC {
cbc := new(MQCBC)
return cbc
}

/*
Since we do not create the structure, there's no conversion for it into
a C format
*/
func copyCBCtoC(mqcbc *C.MQCBC, gocbc *MQCBC) {
return
}

/*
But we do need a conversion process from C
*/
func copyCBCfromC(mqcbc *C.MQCBC, gocbc *MQCBC) {
gocbc.CallType = int32(mqcbc.CallType)
gocbc.State = int32(mqcbc.State)
gocbc.DataLength = int32(mqcbc.DataLength)
gocbc.BufferLength = int32(mqcbc.BufferLength)
gocbc.Flags = int32(mqcbc.Flags)
gocbc.ReconnectDelay = int32(mqcbc.ReconnectDelay)
// ConnectionArea and CallbackArea are restored outside this function

return
}
Loading