<repo_name>springbootbuch/messaging_jms<file_sep>/payment_service/src/main/java/de/springbootbuch/messaging_jms/payment_service/Application.java
package de.springbootbuch.messaging_jms.payment_service;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.jackson.JsonComponentModule;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.JmsListenerConfigurer;
import org.springframework.jms.config.SimpleJmsListenerEndpoint;
import org.springframework.jms.listener.adapter.MessageListenerAdapter;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;

/**
 * Part of springbootbuch.de.
 *
 * @author <NAME>
 * @author @rotnroll666
 */
@SpringBootApplication
public class Application {

	public static void main(String[] args) throws InterruptedException {
		SpringApplication.run(Application.class, args);
	}

	@Bean
	ObjectMapper objectMapper(
		final JsonComponentModule jsonComponentModule
	) {
		final ObjectMapper objectMapper = new ObjectMapper();
		objectMapper.registerModule(jsonComponentModule);
		return objectMapper;
	}

	@Bean
	FilmReturnedEventReceiver filmReturnedEventReceiver() {
		return new FilmReturnedEventReceiver();
	}

	@Bean
	MessageConverter filmReturnedEventConverter(
		final ObjectMapper objectMapper
	) {
		MappingJackson2MessageConverter converter 
			= new MappingJackson2MessageConverter();
		converter.setObjectMapper(objectMapper);
		converter.setTargetType(MessageType.TEXT);
		converter.setTypeIdPropertyName("eventType");
		final Map<String, Class<?>> t 
			= new HashMap<>();
		t.put("FilmReturnedEvent", 
			FilmReturnedEvent.class);
		converter.setTypeIdMappings(t);
		return converter;
	}

	@Bean
	MessageListenerAdapter filmReturnedEventListener(
		final MessageConverter filmReturnedEventConverter,
		final FilmReturnedEventReceiver filmReturnedEventReceiver
	) {
		final MessageListenerAdapter adapter
			= new MessageListenerAdapter(
				filmReturnedEventReceiver);
		adapter
			.setDefaultListenerMethod("filmReturned");
		adapter
			.setMessageConverter(filmReturnedEventConverter);
		return adapter;
	}

	@Bean
	JmsListenerConfigurer jmsListenerConfigurer(
		MessageListenerAdapter filmReturnedEventListener
	) {
		return registrar -> {
			final SimpleJmsListenerEndpoint rv
				= new SimpleJmsListenerEndpoint();
			rv.setId("returned-film-events-receiver");
			rv.setMessageListener(filmReturnedEventListener);
			rv.setDestination("returned-film-events");

			registrar.registerEndpoint(rv);
		};
	}
}
<file_sep>/simple/src/main/resources/application-unused.properties
spring.activemq.broker-url = tcp://192.168.1.210:9876
spring.activemq.user = user
spring.activemq.password = <PASSWORD>
<file_sep>/simple/src/main/java/de/springbootbuch/messaging_jms/simple/GreetingService.java
package de.springbootbuch.messaging_jms.simple;

import org.springframework.jms.core.JmsTemplate;

/**
 * Part of springbootbuch.de.
 *
 * @author <NAME>
 * @author @rotnroll666
 */
public class GreetingService {

	private final JmsTemplate jmsTemplate;

	public GreetingService(JmsTemplate jmsTemplate) {
		this.jmsTemplate = jmsTemplate;
	}

	public void sendGreeting(final String greeting) {
		this.jmsTemplate.send("greetings-topic", session
			-> session.createTextMessage(greeting)
		);
	}
}
<file_sep>/simple/src/main/resources/application-pubsub.properties
spring.jms.pub-sub-domain = true
<file_sep>/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.0</version>
        <relativePath/>
	</parent>

	<groupId>de.springbootbuch</groupId>
	<artifactId>messaging_jms</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>pom</packaging>

	<name>messaging_jms</name>

	<modules>
		<module>film_rental</module>
		<module>payment_service</module>
		<module>simple</module>
	</modules>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-activemq</artifactId>
		</dependency>
	</dependencies>

	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<configuration>
						<compilerArgs>
							<arg>-parameters</arg>
							<arg>-Xlint:unchecked</arg>
						</compilerArgs>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
		<plugins>
			<plugin>
				<groupId>io.fabric8</groupId>
				<artifactId>docker-maven-plugin</artifactId>
				<version>0.21.0</version>
				<inherited>false</inherited>
				<configuration>
					<images>
						<image>
							<name>rmohr/activemq:5.14.3-alpine</name>
							<alias>activemq</alias>
							<run>
								<ports>
									<port>61616:61616</port>
									<port>8161:8161</port>
								</ports>
								<wait>
									<log>Apache ActiveMQ .* started</log>
								</wait>
							</run>
						</image>
					</images>
				</configuration>
			</plugin>
		</plugins>
	</build>

	<profiles>
		<profile>
			<id>use-docker-for-it</id>
			<activation>
				<activeByDefault>true</activeByDefault>
			</activation>
			<build>
				<plugins>
					<plugin>
						<groupId>io.fabric8</groupId>
						<artifactId>docker-maven-plugin</artifactId>
						<executions>
							<!-- Runs before and after integration test -->
							<execution>
								<id>prepare-activemq</id>
								<phase>pre-integration-test</phase>
								<goals>
									<goal>start</goal>
								</goals>
								<configuration>
									<images>
										<image>
											<alias>activemq</alias>
											<run>
												<ports>
													<port>activemq-it.port:61616</port>
												</ports>
											</run>
										</image>
									</images>
								</configuration>
							</execution>
							<execution>
								<id>remove-prepare</id>
								<phase>post-integration-test</phase>
								<goals>
									<goal>stop</goal>
								</goals>
							</execution>
						</executions>
					</plugin>
				</plugins>
			</build>
		</profile>
	</profiles>
</project>
<file_sep>/simple/src/main/java/de/springbootbuch/messaging_jms/simple/DestinationResolverConfig.java
package de.springbootbuch.messaging_jms.simple;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jms.support.destination.DestinationResolver;

/**
 * Part of springbootbuch.de.
 *
 * @author <NAME>
 * @author @rotnroll666
 */
@Profile("destination-router")
@Configuration
public class DestinationResolverConfig {

	@Bean
	public DestinationResolver destinationResolver() {
		return (session, destinationName, pubSubDomain) -> {
			if (destinationName.endsWith("queue")) {
				return session.createQueue(destinationName);
			} else if (destinationName.endsWith("topic")) {
				return session.createTopic(destinationName);
			}
			throw new RuntimeException(
				"Invalid destination: " + destinationName);
		};
	}
}<file_sep>/film_rental/src/main/resources/application.properties
spring.activemq.in-memory = false
<file_sep>/README.md
# messaging_jms

This project needs a local ActiveMQ instance. If you don't have one, you can start one via Docker and Maven:

```
./mvnw docker:run
```

## Simple examples

Goto module `simple`.

Use default Spring boot configuration and talk to a queue domain

```
./mvnw spring-boot:run
```

See the effects of setting `pub-sub-domain` to true

```
./mvnw spring-boot:run -Dspring-boot.run.profiles=pubsub
```

Alternative destination name based routing in effect:

```
./mvnw spring-boot:run -Dspring-boot.run.profiles=destination-router
```

Alternative destination management based on separate templates

```
./mvnw spring-boot:run -Dspring-boot.run.profiles=distinct-infrastructure
```

## Complex scenario

Start the moduls `film_rental` and `payment` via 

```
cd film_rental
./mvnw spring-boot:run
```

And in another shell

```
cd payment_service
./mvnw spring-boot:run
```

You then can post to the rental service like

```
curl -X "POST" "http://localhost:8080/returnedFilms" \
     -H "Content-Type: application/json; charset=utf-8" \
     -d $'{
  "title": "One Flew Over the Cuckoo\'s Nest"
}'
```

and see the film being billed in the the payment service.<file_sep>/simple/src/main/java/de/springbootbuch/messaging_jms/simple/Application.java
package de.springbootbuch.messaging_jms.simple;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.core.JmsTemplate;

/**
 * Part of springbootbuch.de.
 *
 * @author <NAME>
 * @author @rotnroll666
 */
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
	
	@Bean
	@ConditionalOnMissingBean(GreetingService.class)
	public GreetingService greetingService(final JmsTemplate jmsTemplate) {
		return new GreetingService(jmsTemplate);
	}
}<file_sep>/simple/src/main/java/de/springbootbuch/messaging_jms/simple/DistinctInfrastructureConfig.java
package de.springbootbuch.messaging_jms.simple;

import javax.jms.ConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;

/**
 * Part of springbootbuch.de.
 *
 * @author <NAME>
 * @author @rotnroll666
 */
@Profile("distinct-infrastructure")
@Configuration
public class DistinctInfrastructureConfig {

	@Bean
	public JmsListenerContainerFactory<?> topicContainerFactory(
		DefaultJmsListenerContainerFactoryConfigurer configurer,
		ConnectionFactory connectionFactory
	) {
		DefaultJmsListenerContainerFactory containerFactory
			= new DefaultJmsListenerContainerFactory();

		configurer
			.configure(containerFactory, connectionFactory);
		containerFactory.setPubSubDomain(true);
		return containerFactory;
	}

	@Bean
	public JmsListenerContainerFactory<?> queueContainerFactory(
		DefaultJmsListenerContainerFactoryConfigurer configurer,
		ConnectionFactory connectionFactory
	) {
		DefaultJmsListenerContainerFactory containerFactory
			= new DefaultJmsListenerContainerFactory();

		configurer.configure(containerFactory, connectionFactory);
		return containerFactory;
	}

	@Bean
	public JmsTemplate topicJmsTemplate(
		ConnectionFactory connectionFactory
	) {
		JmsTemplate jmsTemplate 
			= new JmsTemplate(connectionFactory);
		jmsTemplate.setPubSubDomain(true);
		return jmsTemplate;
	}

	@Bean
	public JmsTemplate queueJmsTemplate(
		ConnectionFactory connectionFactory
	) {
		return new JmsTemplate(connectionFactory);
	}
	
	@Bean
	public GreetingService greetingService(final JmsTemplate topicJmsTemplate) {
		return new GreetingService(topicJmsTemplate);
	}

	@Bean
	AnnotedGreetingListenerAlt annotedGreetingListenerAlt() {
		return new AnnotedGreetingListenerAlt();
	}
	
	static class AnnotedGreetingListenerAlt {

		private static final Logger LOG = LoggerFactory
			.getLogger(AnnotedGreetingListenerAlt.class);

		@JmsListener(
			destination = "greetings-topic",
			containerFactory = "topicContainerFactory"
		)
		public void onGreeting(final String greeting) {
			LOG.info("Received greeting {}", greeting);
		}
	}
}
