Files
mq-container/vendor/github.com/ibm-messaging/mq-golang/ibmmq/mqi.go
Stephen Marshall a4b9a9abaf Initial delivery of metrics code (#81)
* Initial delivery of metrics code

* Fix build issues

* Fix build issue with go vet
2018-05-24 09:15:12 +01:00

561 lines
12 KiB
Go

/*
Package ibmmq provides a wrapper to a subset of the IBM MQ
procedural interface (the MQI).
In this initial implementation not all the MQI verbs are
included, but it does have the core operations required to
put and get messages and work with topics.
The verbs are given mixed case names without MQ - Open instead
of MQOPEN etc.
If an MQI call returns MQCC_FAILED or MQCC_WARNING, a custom error
type is returned containing the MQCC/MQRC values as
a formatted string. Use mqreturn:= err(*ibmmq.MQReturn) to access
the particular MQRC or MQCC values.
The build directives for Windows assume the header and library files have
been copied to a temporary location, because the default paths are not
acceptable to Go (it does not like spaces or special characters like ~).
Note: This problem appears to have been fixed in Go 1.9, and once that
is fully available, the directives will be changed to a more reasonable
path in this file. For example
cgo windows CFLAGS -I"c:/Program Files/IBM/MQ/tools/c/include" -m64
The build directives for Linux assume the default MQ installation path
in /opt/mqm. These would need to be changed in this file if you use a
non-default path.
*/
package ibmmq
/*
Copyright (c) IBM Corporation 2016
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
Contributors:
Mark Taylor - Initial Contribution
*/
/*
#cgo !windows CFLAGS: -I/opt/mqm/inc -D_REENTRANT
#cgo windows CFLAGS: -I"C:/Program Files/IBM/MQ/Tools/c/include"
#cgo !windows LDFLAGS: -L/opt/mqm/lib64 -lmqm_r -Wl,-rpath=/opt/mqm/lib64 -Wl,-rpath=/usr/lib64
#cgo windows LDFLAGS: -L "C:/Program Files/IBM/MQ/bin64" -lmqm
#include <stdlib.h>
#include <string.h>
#include <cmqc.h>
#include <cmqxc.h>
*/
import "C"
import (
"encoding/binary"
"unsafe"
)
/*
This file contains the C wrappers, calling out to structure-specific
functions where necessary.
Define some basic types to hold the
references to MQ objects - hconn, hobj - and
a simple way to pass the combination of MQCC/MQRC
returned from MQI verbs
The object name is copied into the structures only
for convenience. It's not really needed, but
it can sometimes be nice to print which queue an hObj
refers to during debug.
*/
/*
MQQueueManager contains the connection to the queue manager
*/
type MQQueueManager struct {
hConn C.MQHCONN
Name string
}
/*
MQObject contains a reference to an open object and the associated
queue manager
*/
type MQObject struct {
hObj C.MQHOBJ
qMgr *MQQueueManager
Name string
}
/*
MQReturn holds the MQRC and MQCC values returned from an MQI verb. It
implements the Error() function so is returned as the specific error
from the verbs. See the sample programs for how to access the
MQRC/MQCC values in this returned error.
*/
type MQReturn struct {
MQCC int32
MQRC int32
verb string
}
func (e *MQReturn) Error() string {
return mqstrerror(e.verb, C.MQLONG(e.MQCC), C.MQLONG(e.MQRC))
}
/*
* Copy a Go string into a fixed-size C char array such as MQCHAR12
* Once the string has been copied, it can be immediately freed
* Empty strings have first char set to 0 in MQI structures
*/
func setMQIString(a *C.char, v string, l int) {
if len(v) > 0 {
p := C.CString(v)
C.strncpy(a, p, (C.size_t)(l))
C.free(unsafe.Pointer(p))
} else {
*a = 0
}
}
/*
Conn is the function to connect to a queue manager
*/
func Conn(goQMgrName string) (MQQueueManager, error) {
return Connx(goQMgrName, nil)
}
/*
Connx is the extended function to connect to a queue manager.
*/
func Connx(goQMgrName string, gocno *MQCNO) (MQQueueManager, error) {
var mqrc C.MQLONG
var mqcc C.MQLONG
var mqcno C.MQCNO
if (C.MQENC_NATIVE % 2) == 0 { // May be needed for conversion later
endian = binary.LittleEndian
} else {
endian = binary.BigEndian
}
qMgr := MQQueueManager{}
mqQMgrName := unsafe.Pointer(C.CString(goQMgrName))
defer C.free(mqQMgrName)
// Set up a default CNO if not provided.
if gocno == nil {
// Because Go programs are always threaded, and we cannot
// tell on which thread we might get dispatched, allow handles always to
// be shareable.
gocno = NewMQCNO()
gocno.Options = MQCNO_HANDLE_SHARE_NO_BLOCK
} else {
if (gocno.Options & (MQCNO_HANDLE_SHARE_NO_BLOCK |
MQCNO_HANDLE_SHARE_BLOCK)) == 0 {
gocno.Options |= MQCNO_HANDLE_SHARE_NO_BLOCK
}
}
copyCNOtoC(&mqcno, gocno)
C.MQCONNX((*C.MQCHAR)(mqQMgrName), &mqcno, &qMgr.hConn, &mqcc, &mqrc)
if gocno != nil {
copyCNOfromC(&mqcno, gocno)
}
mqreturn := &MQReturn{MQCC: int32(mqcc),
MQRC: int32(mqrc),
verb: "MQCONNX",
}
if mqcc != C.MQCC_OK {
return qMgr, mqreturn
}
qMgr.Name = goQMgrName
return qMgr, nil
}
/*
Disc is the function to disconnect from the queue manager
*/
func (x *MQQueueManager) Disc() error {
var mqrc C.MQLONG
var mqcc C.MQLONG
C.MQDISC(&x.hConn, &mqcc, &mqrc)
mqreturn := MQReturn{MQCC: int32(mqcc),
MQRC: int32(mqrc),
verb: "MQDISC",
}
if mqcc != C.MQCC_OK {
return &mqreturn
}
return nil
}
/*
Open an object such as a queue or topic
*/
func (x *MQQueueManager) Open(good *MQOD, goOpenOptions int32) (MQObject, error) {
var mqrc C.MQLONG
var mqcc C.MQLONG
var mqod C.MQOD
var mqOpenOptions C.MQLONG
object := MQObject{
Name: good.ObjectName,
qMgr: x,
}
copyODtoC(&mqod, good)
mqOpenOptions = C.MQLONG(goOpenOptions)
C.MQOPEN(x.hConn,
(C.PMQVOID)(unsafe.Pointer(&mqod)),
mqOpenOptions,
&object.hObj,
&mqcc,
&mqrc)
copyODfromC(&mqod, good)
mqreturn := MQReturn{MQCC: int32(mqcc),
MQRC: int32(mqrc),
verb: "MQOPEN",
}
if mqcc != C.MQCC_OK {
return object, &mqreturn
}
// ObjectName may have changed because it's a model queue
object.Name = good.ObjectName
return object, nil
}
/*
Close the object
*/
func (object *MQObject) Close(goCloseOptions int32) error {
var mqrc C.MQLONG
var mqcc C.MQLONG
var mqCloseOptions C.MQLONG
mqCloseOptions = C.MQLONG(goCloseOptions)
C.MQCLOSE(object.qMgr.hConn, &object.hObj, mqCloseOptions, &mqcc, &mqrc)
mqreturn := MQReturn{MQCC: int32(mqcc),
MQRC: int32(mqrc),
verb: "MQCLOSE",
}
if mqcc != C.MQCC_OK {
return &mqreturn
}
return nil
}
/*
Sub is the function to subscribe to a topic
*/
func (x *MQQueueManager) Sub(gosd *MQSD, qObject *MQObject) (MQObject, error) {
var mqrc C.MQLONG
var mqcc C.MQLONG
var mqsd C.MQSD
subObject := MQObject{
Name: gosd.ObjectName,
qMgr: x,
}
copySDtoC(&mqsd, gosd)
C.MQSUB(x.hConn,
(C.PMQVOID)(unsafe.Pointer(&mqsd)),
&qObject.hObj,
&subObject.hObj,
&mqcc,
&mqrc)
copySDfromC(&mqsd, gosd)
mqreturn := MQReturn{MQCC: int32(mqcc),
MQRC: int32(mqrc),
verb: "MQSUB",
}
if mqcc != C.MQCC_OK {
return subObject, &mqreturn
}
qObject.qMgr = x // Force the correct hConn for managed objects
return subObject, nil
}
/*
Cmit is the function to commit an in-flight transaction
*/
func (x *MQQueueManager) Cmit() error {
var mqrc C.MQLONG
var mqcc C.MQLONG
C.MQCMIT(x.hConn, &mqcc, &mqrc)
mqreturn := MQReturn{MQCC: int32(mqcc),
MQRC: int32(mqrc),
verb: "MQCMIT",
}
if mqcc != C.MQCC_OK {
return &mqreturn
}
return nil
}
/*
Back is the function to backout an in-flight transaction
*/
func (x *MQQueueManager) Back() error {
var mqrc C.MQLONG
var mqcc C.MQLONG
C.MQBACK(x.hConn, &mqcc, &mqrc)
mqreturn := MQReturn{MQCC: int32(mqcc),
MQRC: int32(mqrc),
verb: "MQBACK",
}
if mqcc != C.MQCC_OK {
return &mqreturn
}
return nil
}
/*
Put a message to a queue or publish to a topic
*/
func (object MQObject) Put(gomd *MQMD,
gopmo *MQPMO, buffer []byte) error {
var mqrc C.MQLONG
var mqcc C.MQLONG
var mqmd C.MQMD
var mqpmo C.MQPMO
var ptr C.PMQVOID
bufflen := len(buffer)
copyMDtoC(&mqmd, gomd)
copyPMOtoC(&mqpmo, gopmo)
if bufflen > 0 {
ptr = (C.PMQVOID)(unsafe.Pointer(&buffer[0]))
} else {
ptr = nil
}
C.MQPUT(object.qMgr.hConn, object.hObj, (C.PMQVOID)(unsafe.Pointer(&mqmd)),
(C.PMQVOID)(unsafe.Pointer(&mqpmo)),
(C.MQLONG)(bufflen),
ptr,
&mqcc, &mqrc)
copyMDfromC(&mqmd, gomd)
copyPMOfromC(&mqpmo, gopmo)
mqreturn := MQReturn{MQCC: int32(mqcc),
MQRC: int32(mqrc),
verb: "MQPUT",
}
if mqcc != C.MQCC_OK {
return &mqreturn
}
return nil
}
/*
Put1 puts a single messsage to a queue or topic. Typically used for one-shot
replies where it can be cheaper than multiple Open/Put/Close
sequences
*/
func (x *MQQueueManager) Put1(good *MQOD, gomd *MQMD,
gopmo *MQPMO, buffer []byte) error {
var mqrc C.MQLONG
var mqcc C.MQLONG
var mqmd C.MQMD
var mqpmo C.MQPMO
var mqod C.MQOD
var ptr C.PMQVOID
copyODtoC(&mqod, good)
copyMDtoC(&mqmd, gomd)
copyPMOtoC(&mqpmo, gopmo)
bufflen := len(buffer)
if bufflen > 0 {
ptr = (C.PMQVOID)(unsafe.Pointer(&buffer[0]))
} else {
ptr = nil
}
C.MQPUT1(x.hConn, (C.PMQVOID)(unsafe.Pointer(&mqod)),
(C.PMQVOID)(unsafe.Pointer(&mqmd)),
(C.PMQVOID)(unsafe.Pointer(&mqpmo)),
(C.MQLONG)(bufflen),
ptr,
&mqcc, &mqrc)
copyODfromC(&mqod, good)
copyMDfromC(&mqmd, gomd)
copyPMOfromC(&mqpmo, gopmo)
mqreturn := MQReturn{MQCC: int32(mqcc),
MQRC: int32(mqrc),
verb: "MQPUT1",
}
if mqcc != C.MQCC_OK {
return &mqreturn
}
return nil
}
/*
Get a message from a queue
The length of the retrieved message is returned.
*/
func (object MQObject) Get(gomd *MQMD,
gogmo *MQGMO, buffer []byte) (int, error) {
var mqrc C.MQLONG
var mqcc C.MQLONG
var mqmd C.MQMD
var mqgmo C.MQGMO
var datalen C.MQLONG
var ptr C.PMQVOID
bufflen := len(buffer)
copyMDtoC(&mqmd, gomd)
copyGMOtoC(&mqgmo, gogmo)
if bufflen > 0 {
ptr = (C.PMQVOID)(unsafe.Pointer(&buffer[0]))
} else {
ptr = nil
}
C.MQGET(object.qMgr.hConn, object.hObj, (C.PMQVOID)(unsafe.Pointer(&mqmd)),
(C.PMQVOID)(unsafe.Pointer(&mqgmo)),
(C.MQLONG)(bufflen),
ptr,
&datalen,
&mqcc, &mqrc)
godatalen := int(datalen)
copyMDfromC(&mqmd, gomd)
copyGMOfromC(&mqgmo, gogmo)
mqreturn := MQReturn{MQCC: int32(mqcc),
MQRC: int32(mqrc),
verb: "MQGET",
}
if mqcc != C.MQCC_OK {
return 0, &mqreturn
}
return godatalen, nil
}
/*
Inq is the function to inquire on an attribute of an object
Slices are returned containing the integer attributes, and all the
strings concatenated into a single buffer - the caller needs to know
how long each field in that buffer will be.
The caller passes in how many integer selectors are expected to be
returned, as well as the maximum length of the char buffer to be returned
*/
func (object MQObject) Inq(goSelectors []int32, intAttrCount int, charAttrLen int) ([]int32,
[]byte, error) {
var mqrc C.MQLONG
var mqcc C.MQLONG
var mqCharAttrs C.PMQCHAR
var goCharAttrs []byte
var goIntAttrs []int32
var ptr C.PMQLONG
if intAttrCount > 0 {
goIntAttrs = make([]int32, intAttrCount)
ptr = (C.PMQLONG)(unsafe.Pointer(&goIntAttrs[0]))
} else {
ptr = nil
}
if charAttrLen > 0 {
mqCharAttrs = (C.PMQCHAR)(C.malloc(C.size_t(charAttrLen)))
defer C.free(unsafe.Pointer(mqCharAttrs))
} else {
mqCharAttrs = nil
}
// Pass in the selectors directly
C.MQINQ(object.qMgr.hConn, object.hObj,
C.MQLONG(len(goSelectors)),
C.PMQLONG(unsafe.Pointer(&goSelectors[0])),
C.MQLONG(intAttrCount),
ptr,
C.MQLONG(charAttrLen),
mqCharAttrs,
&mqcc, &mqrc)
mqreturn := MQReturn{MQCC: int32(mqcc),
MQRC: int32(mqrc),
verb: "MQINQ",
}
if mqcc != C.MQCC_OK {
return nil, nil, &mqreturn
}
if charAttrLen > 0 {
goCharAttrs = C.GoBytes(unsafe.Pointer(mqCharAttrs), C.int(charAttrLen))
}
return goIntAttrs, goCharAttrs, nil
}