All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.jguru.shared.messaging.test.jms.artemis.ArtemisMessageBroker.kt Maven / Gradle / Ivy

/*-
 * #%L
 * Nazgul Project: jguru-shared-messaging-jms-test
 * %%
 * Copyright (C) 2018 - 2023 jGuru Europe AB
 * %%
 * Licensed under the jGuru Europe AB license (the "License"), based
 * on Apache License, Version 2.0; you may not use this file except
 * in compliance with the License.
 * 
 * You may obtain a copy of the License at
 * 
 *       http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt
 * 
 * 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.
 * #L%
 */
package se.jguru.shared.messaging.test.jms.artemis

import org.apache.activemq.artemis.api.core.TransportConfiguration
import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl
import org.apache.activemq.artemis.core.registry.MapBindingRegistry
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory
import org.apache.activemq.artemis.jms.server.config.impl.ConnectionFactoryConfigurationImpl
import org.apache.activemq.artemis.jms.server.config.impl.JMSConfigurationImpl
import org.apache.activemq.artemis.jms.server.config.impl.JMSQueueConfigurationImpl
import org.apache.activemq.artemis.jms.server.embedded.EmbeddedJMS
import org.apache.activemq.artemis.spi.core.naming.BindingRegistry
import se.jguru.shared.messaging.test.jms.MessageBroker
import java.io.File
import java.util.HashSet
import java.util.SortedMap
import java.util.TreeMap
import jakarta.jms.ConnectionFactory

/**
 * MessageBroker implementation for Apache Artemis.
 */
class ArtemisMessageBroker @JvmOverloads constructor(
    brokerName: String = DEFAULT_BROKERNAME,
    configurationDirectory: String = DEFAULT_CONFIGURATION_DIRECTORY) : MessageBroker {

  // Internal state
  val jmsServer: EmbeddedJMS
  val brokerName: String
  val registry: BindingRegistry
  val isStarted: Boolean get() = (jmsServer as EmbeddedJMS?)?.jmsServerManager?.isStarted ?: false

  init {

    // a) Create the Artemis configuration
    //
    this.registry = MapBindingRegistry()
    val configuration = ConfigurationImpl()
    configuration.isPersistenceEnabled = false
    configuration.isSecurityEnabled = false
    configuration.journalDirectory = getTargetDirectory().absolutePath + File.separatorChar + configurationDirectory

    val journalDirectory = File(configuration.journalDirectory)
    if(!journalDirectory.exists()) {
      journalDirectory.mkdirs()
    }

    // b) Add a transport. Map it as an acceptor and a connector (using the same id).
    //
    val connectorId = "connector"
    val transportConfigurations = HashSet()
    transportConfigurations.add(TransportConfiguration(NettyAcceptorFactory::class.java.name))

    configuration.acceptorConfigurations = transportConfigurations
    configuration.connectorConfigurations[connectorId] = TransportConfiguration(NettyConnectorFactory::class.java.name)

    // c) Create the JMS configuration
    //
    val jmsConfig = JMSConfigurationImpl()
    jmsConfig.connectionFactoryConfigurations.add(ConnectionFactoryConfigurationImpl()
                                                    .setName(CONFIGURATION_FACTORY_ID)
                                                    .setHA(false)
                                                    .setConnectorNames(connectorId)
                                                    .setBindings("/connection/$CONFIGURATION_FACTORY_ID"))


    val queueConfigurations = jmsConfig.queueConfigurations
    UNIT_TEST_QUEUES.entries.forEach {

      // Create a non-persistent queue without any selectors.
      queueConfigurations.add(
        JMSQueueConfigurationImpl().setName(it.key).setSelector(null).setDurable(false).setBindings(it.value)
      )
    }

    // d) Create the server and assign the broker name
    jmsServer = EmbeddedJMS()
    jmsServer.registry = this.registry
    jmsServer.setConfiguration(configuration)
    jmsServer.setJmsConfiguration(jmsConfig)

    this.brokerName = brokerName
  }

  override val name: String = "Artemis"

  override fun startBroker() {
    jmsServer.start()
    jmsServer.jmsServerManager.activeMQServer.identity = brokerName
  }

  override fun stopBroker() {
    when (isStarted && jmsServer.jmsServerManager != null) {
      true -> jmsServer.stop()
      else -> throw IllegalStateException("Cowardly refusing to stop Broker before starting it.")
    }
  }

  override fun getMessageServerURI(): String = "Irrelevant for Artemis"

  override fun getConnectionFactory(configuration: String): ConnectionFactory {
    return jmsServer.lookup("/connection/$CONFIGURATION_FACTORY_ID") as ConnectionFactory
  }

  companion object {

    /**
     * Default Broker identity.
     */
    @JvmStatic
    val DEFAULT_BROKERNAME = "AbstractArtemisJmsTest_Broker"

    /**
     * Default configuration directory.
     */
    @JvmStatic
    val DEFAULT_CONFIGURATION_DIRECTORY = "artemis/plainconfig"

    /**
     * Default registry name of the hornetq jms ConfigurationFactory.
     */
    @JvmStatic
    val CONFIGURATION_FACTORY_ID = "UnitTestConnectionFactory"

    /**
     * A map relating all pre-defined queue names/ids to their bindings (i.e. JNDI key).
     */
    @JvmStatic
    val UNIT_TEST_QUEUES: SortedMap = TreeMap()
      get() {

        if (field.isEmpty()) {
          field["clientRequestQueue"] = "/queue/client/outboundRequest"
          field["clientResponseQueue"] = "/queue/client/inboundResponse"
          field["serverRequestQueue"] = "/queue/server/inboundRequest"
          field["serverResponseQueue"] = "/queue/server/outboundResponse"
        }

        // All Done.
        return field
      }

    /**
     * Retrieves a File to the target directory.
     *
     * @return the project target directory path, wrapped in a File object.
     */
    @JvmStatic
    fun getTargetDirectory(): File {

      // Use CodeSource
      val location = ArtemisMessageBroker::class.java.protectionDomain.codeSource.location
        ?: throw NullPointerException("CodeSource location not found for " +
                                        "class [${ArtemisMessageBroker::class.java.simpleName}]")

      return File(location.path).parentFile
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy