Spring JMS integration module that simplifies JMS development by providing template-based messaging, message-driven POJOs, declarative transaction management, and comprehensive exception handling
—
Pluggable destination resolution supporting JNDI lookups, dynamic destination creation, bean factory integration, and caching for flexible destination management. Spring JMS provides multiple strategies for resolving JMS destinations to accommodate various deployment environments and naming conventions.
Core strategy interface for resolving destination names to JMS Destination objects, enabling pluggable destination resolution strategies.
/**
* Strategy interface for resolving JMS destination names to Destination objects
*/
public interface DestinationResolver {
/**
* Resolve the given destination name to a JMS Destination
* @param session the current JMS session
* @param destinationName the name of the destination to resolve
* @param pubSubDomain whether the domain is pub-sub (topic) or point-to-point (queue)
* @return the resolved Destination object
* @throws JMSException in case of JMS errors
* @throws DestinationResolutionException in case of resolution errors
*/
Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain)
throws JMSException;
}Default destination resolver that creates destinations dynamically using JMS Session methods, suitable for JMS providers that support dynamic destination creation.
/**
* Default destination resolver that creates destinations dynamically
*/
public class DynamicDestinationResolver implements DestinationResolver {
public DynamicDestinationResolver();
/**
* Resolve destination name by creating queue or topic dynamically
* @param session the JMS session
* @param destinationName the destination name
* @param pubSubDomain true for topics, false for queues
* @return the resolved destination
* @throws JMSException in case of JMS errors
*/
@Override
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain)
throws JMSException;
// Template methods for creating destinations
protected Queue resolveQueueName(Session session, String queueName) throws JMSException;
protected Topic resolveTopicName(Session session, String topicName) throws JMSException;
}JNDI-based destination resolver that looks up destinations from a JNDI directory, commonly used in Java EE application servers.
/**
* Destination resolver that uses JNDI lookups
*/
public class JndiDestinationResolver extends JndiLocatorSupport implements DestinationResolver {
// Constructors
public JndiDestinationResolver();
// JNDI configuration
public void setJndiTemplate(JndiTemplate jndiTemplate);
public void setJndiEnvironment(Properties jndiEnvironment);
public void setResourceRef(boolean resourceRef);
// Caching configuration
public void setCache(boolean cache);
public boolean isCache();
public void clearCache();
// Fallback configuration
public void setFallbackToDynamicDestination(boolean fallbackToDynamicDestination);
public boolean isFallbackToDynamicDestination();
// Lookup methods
protected Destination lookupDestination(String destinationName) throws NamingException;
protected Queue lookupQueue(String queueName) throws NamingException;
protected Topic lookupTopic(String topicName) throws NamingException;
// Resolution method
@Override
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain)
throws JMSException;
}Destination resolver that resolves destinations as Spring-managed beans from the application context, providing integration with Spring's dependency injection.
/**
* Destination resolver that resolves destinations as Spring beans
*/
public class BeanFactoryDestinationResolver implements DestinationResolver, BeanFactoryAware {
public BeanFactoryDestinationResolver();
public BeanFactoryDestinationResolver(BeanFactory beanFactory);
// Bean factory configuration
public void setBeanFactory(BeanFactory beanFactory);
protected BeanFactory getBeanFactory();
// Resolution method
@Override
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain)
throws JMSException;
// Template method for bean lookup
protected Destination resolveDestinationName(String destinationName);
}Decorator for destination resolvers that caches resolved destinations for improved performance by avoiding repeated resolution calls.
/**
* Caching wrapper for destination resolvers
*/
public class CachingDestinationResolver implements DestinationResolver {
// Constructors
public CachingDestinationResolver();
public CachingDestinationResolver(DestinationResolver targetResolver);
// Target resolver configuration
public void setTargetResolver(DestinationResolver targetResolver);
public DestinationResolver getTargetResolver();
// Cache management
public void clearCache();
public int getCacheSize();
// Resolution method with caching
@Override
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain)
throws JMSException;
}Abstract base class for JMS accessor objects that use destination resolvers, providing common configuration for destination resolution and pub/sub domain handling.
/**
* Base class for JMS accessor objects with destination resolution support
*/
public abstract class JmsDestinationAccessor extends JmsAccessor {
// Destination resolver configuration
public void setDestinationResolver(DestinationResolver destinationResolver);
public DestinationResolver getDestinationResolver();
// Pub/Sub domain configuration
public void setPubSubDomain(boolean pubSubDomain);
public boolean isPubSubDomain();
// Destination resolution methods
protected Destination resolveDestinationName(Session session, String destinationName) throws JMSException;
protected Queue resolveQueueName(Session session, String queueName) throws JMSException;
protected Topic resolveTopicName(Session session, String topicName) throws JMSException;
}Exception hierarchy for destination resolution errors with specific error categorization.
/**
* Exception thrown when destination resolution fails
*/
public class DestinationResolutionException extends JmsException {
public DestinationResolutionException(String msg);
public DestinationResolutionException(String msg, Throwable cause);
}Usage Examples:
import org.springframework.jms.support.destination.*;
import javax.naming.Context;
import java.util.Properties;
// Configuration for different destination resolution strategies
@Configuration
public class DestinationConfig {
// Dynamic destination resolver (default)
@Bean
public DestinationResolver dynamicDestinationResolver() {
return new DynamicDestinationResolver();
}
// JNDI destination resolver for Java EE environments
@Bean
public DestinationResolver jndiDestinationResolver() {
JndiDestinationResolver resolver = new JndiDestinationResolver();
resolver.setResourceRef(true); // Use java:comp/env/ prefix
resolver.setCache(true); // Enable caching for performance
resolver.setFallbackToDynamicDestination(true); // Fallback if JNDI lookup fails
// Configure JNDI environment
Properties jndiProps = new Properties();
jndiProps.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.jndi.ActiveMQInitialContextFactory");
jndiProps.setProperty(Context.PROVIDER_URL, "tcp://localhost:61616");
resolver.setJndiEnvironment(jndiProps);
return resolver;
}
// Bean factory destination resolver
@Bean
public DestinationResolver beanFactoryDestinationResolver() {
return new BeanFactoryDestinationResolver();
}
// Caching destination resolver wrapper
@Bean
public DestinationResolver cachingDestinationResolver() {
return new CachingDestinationResolver(jndiDestinationResolver());
}
// Define destinations as Spring beans
@Bean
public Queue orderQueue() {
return new ActiveMQQueue("order.queue");
}
@Bean
public Topic notificationTopic() {
return new ActiveMQTopic("notification.topic");
}
// JmsTemplate with custom destination resolver
@Bean
public JmsTemplate jmsTemplate() {
JmsTemplate template = new JmsTemplate(connectionFactory());
template.setDestinationResolver(cachingDestinationResolver());
return template;
}
// Message listener container with destination resolver
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setDestinationResolver(cachingDestinationResolver());
return factory;
}
}
// Service using destination resolution
@Service
public class MessagingService {
@Autowired
private JmsTemplate jmsTemplate;
@Autowired
private DestinationResolver destinationResolver;
// Using destination names (resolved automatically by JmsTemplate)
public void sendToQueue(String queueName, Object message) {
jmsTemplate.convertAndSend(queueName, message);
}
public void sendToTopic(String topicName, Object message) {
jmsTemplate.setPubSubDomain(true);
jmsTemplate.convertAndSend(topicName, message);
}
// Manual destination resolution
public void sendWithManualResolution(String destinationName, Object message) {
jmsTemplate.send(session -> {
// Manually resolve destination
Destination destination = destinationResolver.resolveDestinationName(
session, destinationName, false); // false = queue, true = topic
MessageProducer producer = session.createProducer(destination);
ObjectMessage objMessage = session.createObjectMessage((Serializable) message);
producer.send(objMessage);
return objMessage;
});
}
}
// Custom destination resolver implementation
@Component
public class CustomDestinationResolver implements DestinationResolver {
private final Map<String, String> destinationMappings = new HashMap<>();
private final DestinationResolver fallbackResolver = new DynamicDestinationResolver();
@PostConstruct
public void initializeMappings() {
// Map logical names to physical destinations
destinationMappings.put("orders", "app.orders.v1");
destinationMappings.put("notifications", "app.notifications.v1");
destinationMappings.put("audit", "app.audit.v1");
}
@Override
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain)
throws JMSException {
// Apply destination mapping
String physicalName = destinationMappings.getOrDefault(destinationName, destinationName);
// Add environment prefix
String environment = System.getProperty("app.environment", "dev");
String resolvedName = environment + "." + physicalName;
// Use fallback resolver for actual destination creation
return fallbackResolver.resolveDestinationName(session, resolvedName, pubSubDomain);
}
}
// Destination resolver with conditional logic
@Component
@Profile("production")
public class ProductionDestinationResolver implements DestinationResolver {
@Autowired
private JndiDestinationResolver jndiResolver;
@Autowired
private DynamicDestinationResolver dynamicResolver;
@Override
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain)
throws JMSException {
// Try JNDI first for production destinations
try {
return jndiResolver.resolveDestinationName(session, destinationName, pubSubDomain);
} catch (DestinationResolutionException e) {
// Fallback to dynamic resolution for temporary destinations
if (destinationName.startsWith("temp.") || destinationName.startsWith("reply.")) {
return dynamicResolver.resolveDestinationName(session, destinationName, pubSubDomain);
}
throw e;
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-springframework--spring-jms