github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/integration/activemq/activemq.go (about)

     1  package activemq
     2  
     3  import (
     4  	"crypto/x509"
     5  	"database/sql"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"os/signal"
    11  	"strconv"
    12  	"syscall"
    13  	"time"
    14  
    15  	"github.com/go-stomp/stomp"
    16  	"github.com/google/uuid"
    17  	"github.com/mdaxf/iac-signalr/signalr"
    18  	"github.com/mdaxf/iac/com"
    19  	dbconn "github.com/mdaxf/iac/databases"
    20  	"github.com/mdaxf/iac/documents"
    21  	"github.com/mdaxf/iac/framework/queue"
    22  	"github.com/mdaxf/iac/logger"
    23  )
    24  
    25  type ActiveMQconfigs struct {
    26  	ActiveMQs []ActiveMQconfig `json:"activemqs"`
    27  	ApiKey    string           `json:"apikey"`
    28  }
    29  
    30  // ActiveMQ struct
    31  type ActiveMQconfig struct {
    32  	Host      string          `json:"host"`
    33  	Port      string          `json:"port"`
    34  	Username  string          `json:"username"`
    35  	Password  string          `json:"passwrod"`
    36  	TLS       string          `json:"tls"`
    37  	TLSVerify bool            `json:"tlsverify"`
    38  	CAPath    string          `json:"CAPath"`
    39  	CertPath  string          `json:"CertPath"`
    40  	KeyPath   string          `json:"keypath"`
    41  	Topics    []ActiveMQtopic `json:"topics"`
    42  }
    43  
    44  type ActiveMQtopic struct {
    45  	Topic    string `json:"topic"`
    46  	Handler  string `json:"handler"`
    47  	SQLQuery string `json:"sqlquery"`
    48  	Mode     string `json:"mode"`
    49  	Type     string `json:"type"`
    50  }
    51  
    52  // ActiveMQ struct
    53  type ActiveMQ struct {
    54  	Config        ActiveMQconfig
    55  	Conn          *stomp.Conn
    56  	Subs          []*stomp.Subscription
    57  	ConnectionID  string
    58  	QueueID       string
    59  	iLog          logger.Log
    60  	Queue         *queue.MessageQueue
    61  	DocDBconn     *documents.DocDB
    62  	DB            *sql.DB
    63  	SignalRClient signalr.Client
    64  	AppServer     string
    65  	ApiKey        string
    66  }
    67  
    68  /*
    69  	type msghandler struct {
    70  		Topic   string
    71  		Handler string
    72  		Message stomp.Message
    73  	}
    74  */
    75  func NewActiveMQConnection(config ActiveMQconfig) *ActiveMQ {
    76  
    77  	activeMQ := connectActiveMQ(config)
    78  
    79  	if activeMQ == nil {
    80  		iLog := logger.Log{ModuleName: logger.Framework, User: "System", ControllerName: "ActiveMQConnection"}
    81  
    82  		iLog.Critical(fmt.Sprintf(("Fail to create activeMQ connection with configuration : %s"), logger.ConvertJson(config)))
    83  
    84  		return nil
    85  	}
    86  	activeMQ.iLog.Debug(fmt.Sprintf("Create ActiveMQ connection successful!"))
    87  
    88  	uuid_ := uuid.New().String()
    89  
    90  	activeMQ.DocDBconn = documents.DocDBCon
    91  	activeMQ.DB = dbconn.DB
    92  	activeMQ.SignalRClient = com.IACMessageBusClient
    93  	activeMQ.Queue = queue.NewMessageQueue(uuid_, "ActiveMQ")
    94  	activeMQ.Queue.DocDBconn = documents.DocDBCon
    95  	activeMQ.Queue.DB = dbconn.DB
    96  	activeMQ.Queue.SignalRClient = com.IACMessageBusClient
    97  
    98  	activeMQ.Subscribes()
    99  
   100  	return activeMQ
   101  }
   102  
   103  func NewActiveMQConnectionExternal(config ActiveMQconfig, docDBconn *documents.DocDB, db *sql.DB, signalRClient signalr.Client) *ActiveMQ {
   104  
   105  	activeMQ := connectActiveMQ(config)
   106  
   107  	if activeMQ == nil {
   108  		iLog := logger.Log{ModuleName: logger.Framework, User: "System", ControllerName: "ActiveMQConnection"}
   109  
   110  		iLog.Critical(fmt.Sprintf(("Fail to create activeMQ connection with configuration : %s"), logger.ConvertJson(config)))
   111  
   112  		return nil
   113  	}
   114  	activeMQ.iLog.Debug(fmt.Sprintf("Create ActiveMQ connection successful!"))
   115  
   116  	uuid_ := uuid.New().String()
   117  	activeMQ.Queue = queue.NewMessageQueue(uuid_, "ActiveMQ")
   118  	activeMQ.Queue.DocDBconn = docDBconn
   119  	activeMQ.Queue.DB = db
   120  	activeMQ.Queue.SignalRClient = signalRClient
   121  
   122  	activeMQ.DocDBconn = docDBconn
   123  	activeMQ.DB = db
   124  	activeMQ.SignalRClient = signalRClient
   125  
   126  	//	activeMQ.Subscribes()
   127  
   128  	return activeMQ
   129  
   130  }
   131  func CheckConnection(activemq ActiveMQ) bool {
   132  	if activemq.Conn == nil {
   133  		return false
   134  	}
   135  
   136  	return true
   137  }
   138  func connectActiveMQ(config ActiveMQconfig) *ActiveMQ {
   139  
   140  	iLog := logger.Log{ModuleName: logger.Framework, User: "System", ControllerName: "ActiveMQConnection"}
   141  
   142  	iLog.Debug(fmt.Sprintf(("Create activeMQ connection with configuration : %s"), logger.ConvertJson(config)))
   143  
   144  	if config.Host == "" {
   145  		iLog.Error("Host is required")
   146  		return nil
   147  	}
   148  
   149  	if config.Port == "" {
   150  		iLog.Error("Port is required")
   151  		return nil
   152  	}
   153  
   154  	var conn *stomp.Conn
   155  	var err error
   156  
   157  	if config.Username == "" && config.TLS == "" {
   158  		conn, err = stomp.Dial(
   159  			"tcp",
   160  			config.Host+":"+config.Port,
   161  			stomp.ConnOpt.AcceptVersion(stomp.V12),
   162  		)
   163  	} else if config.Username != "" && config.TLS == "" {
   164  		conn, err = stomp.Dial(
   165  			"tcp",
   166  			config.Host+":"+config.Port,
   167  			stomp.ConnOpt.Login(config.Username, config.Password),
   168  		)
   169  	} else if config.Username == "" && config.TLS != "" && config.TLSVerify == false {
   170  		conn, err = stomp.Dial(
   171  			"tcp",
   172  			config.Host+":"+config.Port,
   173  			stomp.ConnOpt.AcceptVersion(stomp.V12),
   174  		//	stomp.ConnOpt.TLSConfig(&tls.Config{InsecureSkipVerify: true}), // Set to true for testing purposes only
   175  		)
   176  	} else if config.Username != "" && config.TLS != "" && config.TLSVerify == false {
   177  		conn, err = stomp.Dial(
   178  			"tcp",
   179  			config.Host+":"+config.Port,
   180  			stomp.ConnOpt.Login(config.Username, config.Password),
   181  			stomp.ConnOpt.AcceptVersion(stomp.V12),
   182  		//	stomp.ConnOpt.TLSConfig(&tls.Config{InsecureSkipVerify: true}), // Set to true for testing purposes only
   183  		)
   184  	} else if config.Username == "" && config.TLSVerify == true && config.CAPath != "" && config.CertPath != "" {
   185  		//	cert, err := tls.LoadX509KeyPair(config.CertPath, config.KeyPath)
   186  		if err != nil {
   187  			iLog.Critical(fmt.Sprintf("Failed to load client certificates: %v", err))
   188  			return nil
   189  		}
   190  
   191  		conn, err = stomp.Dial(
   192  			"tcp",
   193  			config.Host+":"+config.Port,
   194  			stomp.ConnOpt.AcceptVersion(stomp.V12),
   195  		/*	stomp.ConnOpt.TLSConfig(&tls.Config{
   196  			InsecureSkipVerify: false, // Set to true for testing purposes only
   197  			RootCAs:            loadCACert(config.CAPath, iLog),
   198  			Certificates:       []tls.Certificate{cert},
   199  		}), */
   200  		)
   201  	} else if config.Username != "" && config.Password != "" && config.TLSVerify == true && config.CAPath != "" && config.CertPath != "" {
   202  		//	cert, err := tls.LoadX509KeyPair(config.CertPath, config.KeyPath)
   203  		if err != nil {
   204  			iLog.Critical(fmt.Sprintf("Failed to load client certificates: %v", err))
   205  			return nil
   206  		}
   207  
   208  		conn, err = stomp.Dial(
   209  			"tcp",
   210  			config.Host+":"+config.Port,
   211  			stomp.ConnOpt.Login(config.Username, config.Password),
   212  			stomp.ConnOpt.AcceptVersion(stomp.V12),
   213  		/*	stomp.ConnOpt.TLSConfig(&tls.Config{
   214  			InsecureSkipVerify: false, // Set to true for testing purposes only
   215  			RootCAs:            loadCACert(config.CAPath, iLog),
   216  			Certificates:       []tls.Certificate{cert},
   217  		}),  */
   218  		)
   219  	} else {
   220  		iLog.Error("Invalid configuration")
   221  		return nil
   222  	}
   223  
   224  	if err != nil {
   225  		iLog.Error(fmt.Sprintf("Error while connecting to ActiveMQ: %s", err.Error()))
   226  		return nil
   227  	}
   228  	activeMQ := &ActiveMQ{
   229  		Config: config,
   230  		Conn:   conn,
   231  		iLog:   iLog,
   232  	}
   233  
   234  	return activeMQ
   235  
   236  }
   237  
   238  func loadCACert(caCertFile string, iLog logger.Log) *x509.CertPool {
   239  	caCert, err := ioutil.ReadFile(caCertFile)
   240  	if err != nil {
   241  		iLog.Error(fmt.Sprintf("Failed to read CA certificate: %v", err))
   242  	}
   243  	caCertPool := x509.NewCertPool()
   244  	caCertPool.AppendCertsFromPEM(caCert)
   245  	return caCertPool
   246  }
   247  
   248  func (a *ActiveMQ) Subscribes() {
   249  	/*
   250  		messageChannel := make(chan msghandler)
   251  
   252  		messageHandler := func(msg stomp.Message) {
   253  			messageChannel <- msg
   254  		}  */
   255  
   256  	for _, item := range a.Config.Topics {
   257  		topic := item.Topic
   258  		handler := item.Handler
   259  		//	mode := item.Mode
   260  		executetype := item.Type
   261  
   262  		sub, err := a.Conn.Subscribe(topic, stomp.AckAuto)
   263  		if err != nil {
   264  			a.iLog.Error(fmt.Sprintf("Error while subscribing to topic %s: %s", topic, err.Error()))
   265  		}
   266  		a.Subs = append(a.Subs, sub)
   267  		go func() {
   268  			if executetype == "local" {
   269  				msgID := 0
   270  				for {
   271  					msg := <-sub.C
   272  					msgID += 1
   273  					fmt.Println("Received message", string(msg.Body))
   274  					message := queue.Message{
   275  						Id:        strconv.FormatUint(uint64(msgID), 10),
   276  						UUID:      uuid.New().String(),
   277  						Retry:     3,
   278  						Execute:   0,
   279  						Topic:     topic,
   280  						PayLoad:   msg.Body,
   281  						Handler:   handler,
   282  						CreatedOn: time.Now().UTC(),
   283  					}
   284  
   285  					a.iLog.Debug(fmt.Sprintf("Push message %v to queue: %s", message, a.Queue.QueueID))
   286  					a.Queue.Push(message)
   287  				}
   288  			} else {
   289  				for {
   290  					msg := <-sub.C
   291  					fmt.Println("Received message", string(msg.Body))
   292  					a.CallWebService(msg, topic, handler)
   293  				}
   294  			}
   295  		}()
   296  	}
   297  
   298  	a.waitForTerminationSignal()
   299  
   300  }
   301  
   302  func (a *ActiveMQ) CallWebService(msg *stomp.Message, topic string, handler string) {
   303  
   304  	method := "POST"
   305  	url := a.AppServer + "/trancode/execute"
   306  
   307  	var result map[string]interface{}
   308  	err := json.Unmarshal(msg.Body, &result)
   309  	if err != nil {
   310  		a.iLog.Error(fmt.Sprintf("Error:", err))
   311  		return
   312  	}
   313  	var inputs map[string]interface{}
   314  
   315  	inputs["Payload"] = result
   316  	inputs["Topic"] = topic
   317  
   318  	msgdata := make(map[string]interface{})
   319  	msgdata["TranCode"] = handler
   320  	msgdata["Inputs"] = inputs
   321  
   322  	headers := make(map[string]string)
   323  	headers["Content-Type"] = "application/json"
   324  	headers["Authorization"] = "apikey " + a.ApiKey
   325  
   326  	result, err = com.CallWebService(url, method, msgdata, headers)
   327  
   328  	if err != nil {
   329  		a.iLog.Error(fmt.Sprintf("Error in WebServiceCallFunc.Execute: %s", err))
   330  		return
   331  	}
   332  
   333  	a.iLog.Debug(fmt.Sprintf("Response data: %v", result))
   334  
   335  }
   336  
   337  func (a *ActiveMQ) waitForTerminationSignal() {
   338  	c := make(chan os.Signal, 1)
   339  	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
   340  	<-c
   341  	fmt.Println("\nShutting down...")
   342  
   343  	for _, sub := range a.Subs {
   344  		sub.Unsubscribe()
   345  	}
   346  
   347  	a.Conn.Disconnect()
   348  	time.Sleep(2 * time.Second) // Add any cleanup or graceful shutdown logic here
   349  	os.Exit(0)
   350  }