Skip to content

Commit 6f8a53a

Browse files
committed
Add MQSET implementation
1 parent d8ac717 commit 6f8a53a

File tree

6 files changed

+243
-15
lines changed

6 files changed

+243
-15
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
* Update MQI header files for MQ V9.1.1 and give more platform variations
88
* Add support for MQSTAT and MQSUBRQ functions
99
* Add support and sample for Message Property functions
10-
* Add InqMap as alternative (simpler) MQINQ operation
10+
* Add InqMap as alternative (simpler) MQINQ operation. Inq() should be considered deprecated
11+
* Add support for MQSET function
1112

1213
## November 2018 - v3.1.0
1314
* Added functions to mqmetric to issue DISPLAY QSTATUS for additional stats

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,10 @@ At this point, you should have a compiled copy of the program in `$GOPATH/bin`.
123123

124124
## Limitations
125125

126-
Not all of the MQI verbs are available through the `ibmmq` package. This
127-
implementation concentrates on the core API calls needed to put and get messages.
126+
Almost all of the MQI verbs are now available through the `ibmmq` package.
128127
Currently unavailable verbs include:
129128

130-
* MQSET
131-
* MQCB
129+
* MQCB/MQCTL
132130

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

ibmmq/mqi.go

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -598,9 +598,9 @@ how long each field in that buffer will be.
598598
The caller passes in how many integer selectors are expected to be
599599
returned, as well as the maximum length of the char buffer to be returned
600600
601-
This function is a direct mapping of the MQI C function. It should be considered
601+
Deprecated: This function is a direct mapping of the MQI C function. It should be considered
602602
deprecated. In preference, use the InqMap function which provides a more convenient
603-
API.
603+
API. In a future version of this package, Inq will be replaced by InqMap
604604
*/
605605
func (object MQObject) Inq(goSelectors []int32, intAttrCount int, charAttrLen int) ([]int32,
606606
[]byte, error) {
@@ -656,6 +656,8 @@ func (object MQObject) Inq(goSelectors []int32, intAttrCount int, charAttrLen in
656656
* and the return value consists of a map whose elements are
657657
* a) accessed via the selector
658658
* b) varying datatype (integer, string, string array) based on the selector
659+
* In a future breaking update, this function will become the default Inq()
660+
* implementation.
659661
*/
660662
func (object MQObject) InqMap(goSelectors []int32) (map[int32]interface{}, error) {
661663
var mqrc C.MQLONG
@@ -701,7 +703,7 @@ func (object MQObject) InqMap(goSelectors []int32) (map[int32]interface{}, error
701703
return nil, &mqreturn
702704
}
703705

704-
// Create a map of the selectors to the returned values
706+
// Create a map of the selectors to the returned values
705707
returnedMap := make(map[int32]interface{})
706708

707709
// Get access to the returned character data
@@ -736,7 +738,7 @@ func (object MQObject) InqMap(goSelectors []int32) (map[int32]interface{}, error
736738
charLength = C.MQ_OBJECT_NAME_LENGTH
737739
names := make([]string, c)
738740
for j := 0; j < c; j++ {
739-
name := string(goCharAttrs[charOffset:charOffset+charLength])
741+
name := string(goCharAttrs[charOffset : charOffset+charLength])
740742
idx := strings.IndexByte(name, 0)
741743
if idx != -1 {
742744
name = name[0:idx]
@@ -750,7 +752,7 @@ func (object MQObject) InqMap(goSelectors []int32) (map[int32]interface{}, error
750752
}
751753
} else {
752754
charLength = getAttrLength(s)
753-
name := string(goCharAttrs[charOffset:charOffset+charLength])
755+
name := string(goCharAttrs[charOffset : charOffset+charLength])
754756
idx := strings.IndexByte(name, 0)
755757
if idx != -1 {
756758
name = name[0:idx]
@@ -765,6 +767,108 @@ func (object MQObject) InqMap(goSelectors []int32) (map[int32]interface{}, error
765767
return returnedMap, nil
766768
}
767769

770+
/*
771+
* Set is the function that wraps MQSET. The single parameter is a map whose
772+
* elements contain an MQIA/MQCA selector with either a string or an int32 for
773+
* the value.
774+
*/
775+
func (object MQObject) Set(goSelectors map[int32]interface{}) error {
776+
var mqrc C.MQLONG
777+
var mqcc C.MQLONG
778+
779+
var charAttrs []byte
780+
var charAttrsPtr C.PMQCHAR
781+
var intAttrs []int32
782+
var intAttrsPtr C.PMQLONG
783+
var charOffset int
784+
var charLength int
785+
786+
// Pass through the map twice. First time lets us
787+
// create an array of selector names from map keys which is then
788+
// used to calculate the character buffer that's needed
789+
selectors := make([]int32, len(goSelectors))
790+
i := 0
791+
for k, _ := range goSelectors {
792+
selectors[i] = k
793+
i++
794+
}
795+
796+
intAttrCount, _, charAttrLen := getAttrInfo(selectors)
797+
798+
// Create the areas to be used for the separate char and int values
799+
if intAttrCount > 0 {
800+
intAttrs = make([]int32, intAttrCount)
801+
intAttrsPtr = (C.PMQLONG)(unsafe.Pointer(&intAttrs[0]))
802+
} else {
803+
intAttrsPtr = nil
804+
}
805+
806+
if charAttrLen > 0 {
807+
charAttrs = make([]byte, charAttrLen)
808+
charAttrsPtr = (C.PMQCHAR)(unsafe.Pointer(&charAttrs[0]))
809+
} else {
810+
charAttrsPtr = nil
811+
}
812+
813+
// Walk through the map a second time
814+
charOffset = 0
815+
intAttr := 0
816+
for i := 0; i < len(selectors); i++ {
817+
s := selectors[i]
818+
if s >= C.MQCA_FIRST && s <= C.MQCA_LAST {
819+
// The character processing is a bit OTT since there is in reality
820+
// only a single attribute that can ever be SET. But a general purpose
821+
// function looks more like the MQINQ operation
822+
v := goSelectors[s].(string)
823+
charLength = getAttrLength(s)
824+
vBytes := []byte(v)
825+
b := byte(0)
826+
for j := 0; j < charLength; j++ {
827+
if j < len(vBytes) {
828+
b = vBytes[j]
829+
} else {
830+
b = 0
831+
}
832+
charAttrs[charOffset+j] = b
833+
}
834+
charOffset += charLength
835+
} else if s >= C.MQIA_FIRST && s <= C.MQIA_LAST {
836+
vv := int32(0)
837+
v := goSelectors[s]
838+
// Force the returned value from the map to be int32 because we
839+
// can't check it at compile time.
840+
if _,ok := v.(int32);ok {
841+
vv = v.(int32)
842+
} else if _,ok := v.(int);ok {
843+
vv = int32(v.(int))
844+
}
845+
intAttrs[intAttr] = vv
846+
intAttr++
847+
}
848+
}
849+
850+
// Pass in the selectors
851+
C.MQSET(object.qMgr.hConn, object.hObj,
852+
C.MQLONG(len(selectors)),
853+
C.PMQLONG(unsafe.Pointer(&selectors[0])),
854+
C.MQLONG(intAttrCount),
855+
intAttrsPtr,
856+
C.MQLONG(charAttrLen),
857+
charAttrsPtr,
858+
&mqcc, &mqrc)
859+
860+
mqreturn := MQReturn{MQCC: int32(mqcc),
861+
MQRC: int32(mqrc),
862+
verb: "MQSET",
863+
}
864+
865+
if mqcc != C.MQCC_OK {
866+
return &mqreturn
867+
}
868+
869+
return nil
870+
}
871+
768872
/*********** Message Handles and Properties ****************/
769873

770874
/*

ibmmq/mqiattrs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ var mqInqLength = map[int32]int32{
6464
C.MQCA_LU_NAME: C.MQ_LU_NAME_LENGTH,
6565
C.MQCA_NAMELIST_DESC: C.MQ_NAMELIST_DESC_LENGTH,
6666
C.MQCA_NAMELIST_NAME: C.MQ_NAMELIST_NAME_LENGTH,
67-
C.MQCA_NAMES: C.MQ_OBJECT_NAME_LENGTH * 256, // TODO: split these out
67+
C.MQCA_NAMES: C.MQ_OBJECT_NAME_LENGTH * 256, // Maximum length to allocate
6868
C.MQCA_PARENT: C.MQ_Q_MGR_NAME_LENGTH,
6969
C.MQCA_PROCESS_DESC: C.MQ_PROCESS_DESC_LENGTH,
7070
C.MQCA_PROCESS_NAME: C.MQ_PROCESS_NAME_LENGTH,

samples/amqsinq.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ func inquire(obj ibmmq.MQObject, selectors []int32) {
5151
fmt.Printf("----------- %s ----------\n", obj.Name)
5252
for s, v := range values {
5353
// Having got the values, print the selector number and name, along with the value
54-
ss := ibmmq.MQItoString("CA",int(s))
54+
ss := ibmmq.MQItoString("CA", int(s))
5555
if ss == "" {
56-
ss = ibmmq.MQItoString("IA",int(s))
56+
ss = ibmmq.MQItoString("IA", int(s))
5757
}
58-
fmt.Printf("%-4d %-32.32s\t'%v'\n", s, ss,v)
58+
fmt.Printf("%-4d %-32.32s\t'%v'\n", s, ss, v)
5959
}
6060
}
6161
}
@@ -142,7 +142,6 @@ func main() {
142142
}
143143
}
144144

145-
146145
// Open of another object - a namelist. This has the ability to extract
147146
// the list of queues it refers to.
148147
if err == nil {

samples/amqsset.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* This is an example of a Go program to set some attributes of an IBM MQ
3+
* queue through the MQSET verb. The attributes that can be set are limited;
4+
* those limitations are described in the MQSET documentation.
5+
*
6+
* The queue and queue manager name can be given as parameters on the
7+
* command line. Defaults are coded in the program.
8+
*
9+
*
10+
*/
11+
package main
12+
13+
/*
14+
Copyright (c) IBM Corporation 2018
15+
16+
Licensed under the Apache License, Version 2.0 (the "License");
17+
you may not use this file except in compliance with the License.
18+
You may obtain a copy of the License at
19+
20+
http://www.apache.org/licenses/LICENSE-2.0
21+
22+
Unless required by applicable law or agreed to in writing, software
23+
distributed under the License is distributed on an "AS IS" BASIS,
24+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25+
See the License for the specific language governing permissions and
26+
limitations under the license.
27+
28+
Contributors:
29+
Mark Taylor - Initial Contribution
30+
*/
31+
32+
import (
33+
"fmt"
34+
"os"
35+
"time"
36+
37+
"github.com/ibm-messaging/mq-golang/ibmmq"
38+
)
39+
40+
var qMgrObject ibmmq.MQObject
41+
var object ibmmq.MQObject
42+
43+
/*
44+
* This is an example of how to call MQSET
45+
*/
46+
func setAttributes(obj ibmmq.MQObject) {
47+
// Create a map containing the selectors and their values. The values must be
48+
// a string (for MQCA attributes) or an int/int32 for the MQIA values.
49+
50+
// The value being set in the TRIGDATA attribute has a timestamp so you can
51+
// see if it is successfully changed
52+
selectors := map[int32]interface{}{
53+
ibmmq.MQIA_INHIBIT_PUT: ibmmq.MQQA_PUT_INHIBITED,
54+
ibmmq.MQIA_TRIGGER_CONTROL: ibmmq.MQTC_ON,
55+
ibmmq.MQCA_TRIGGER_DATA: "Data set at " + time.Now().Format(time.RFC3339)}
56+
57+
// And call the MQI
58+
err := obj.Set(selectors)
59+
if err != nil {
60+
fmt.Println(err)
61+
} else {
62+
fmt.Println("Object attributes successfully changed")
63+
}
64+
}
65+
66+
func main() {
67+
// The default queue manager and a queue to be used. These can be overridden on command line.
68+
qMgrName := "QM1"
69+
qName := "DEV.QUEUE.1"
70+
71+
qMgrConnected := false
72+
73+
fmt.Println("Sample AMQSSET.GO start")
74+
75+
// Get the object names from command line for overriding
76+
// the defaults. Parameters are not required.
77+
if len(os.Args) >= 2 {
78+
qName = os.Args[1]
79+
}
80+
81+
if len(os.Args) >= 3 {
82+
qMgrName = os.Args[2]
83+
}
84+
85+
qMgrObject, err := ibmmq.Conn(qMgrName)
86+
if err != nil {
87+
fmt.Println(err)
88+
} else {
89+
qMgrConnected = true
90+
fmt.Printf("Connected to queue manager %s\n", qMgrName)
91+
}
92+
93+
// Open a queue with the option to say it will be modified
94+
if err == nil {
95+
// Create the Object Descriptor that allows us to give the object name
96+
mqod := ibmmq.NewMQOD()
97+
98+
// We have to say how we are going to use this object. The MQOO_SET flag
99+
// says that it will be used for an MQSET operation
100+
openOptions := ibmmq.MQOO_FAIL_IF_QUIESCING + ibmmq.MQOO_SET
101+
mqod.ObjectType = ibmmq.MQOT_Q
102+
mqod.ObjectName = qName
103+
104+
object, err = qMgrObject.Open(mqod, openOptions)
105+
if err != nil {
106+
fmt.Println(err)
107+
} else {
108+
setAttributes(object)
109+
object.Close(0)
110+
}
111+
}
112+
113+
// Disconnect from the queue manager
114+
if qMgrConnected {
115+
err = qMgrObject.Disc()
116+
fmt.Printf("Disconnected from queue manager %s\n", qMgrName)
117+
}
118+
119+
// Exit with any return code extracted from the failing MQI call.
120+
if err == nil {
121+
os.Exit(0)
122+
} else {
123+
mqret := err.(*ibmmq.MQReturn)
124+
os.Exit((int)(mqret.MQCC))
125+
}
126+
}

0 commit comments

Comments
 (0)