001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.jms.support.destination; 018 019import java.util.Map; 020import java.util.concurrent.ConcurrentHashMap; 021 022import javax.jms.Destination; 023import javax.jms.JMSException; 024import javax.jms.Queue; 025import javax.jms.Session; 026import javax.jms.Topic; 027import javax.naming.NamingException; 028 029import org.springframework.jndi.JndiLocatorSupport; 030import org.springframework.lang.Nullable; 031import org.springframework.util.Assert; 032 033/** 034 * {@link DestinationResolver} implementation which interprets destination names 035 * as JNDI locations (with a configurable fallback strategy). 036 * 037 * <p>Allows for customizing the JNDI environment if necessary, for example 038 * specifying appropriate JNDI environment properties. 039 * 040 * <p>Dynamic queues and topics get cached by destination name. As a consequence, 041 * you need to use unique destination names across both queues and topics. 042 * Caching can be turned off through the {@link #setCache "cache"} flag. 043 * 044 * <p>Note that the fallback to resolution of dynamic destinations 045 * is turned <i>off</i> by default. Switch the 046 * {@link #setFallbackToDynamicDestination "fallbackToDynamicDestination"} 047 * flag on to enable this functionality. 048 * 049 * @author Mark Pollack 050 * @author Juergen Hoeller 051 * @since 1.1 052 * @see #setJndiTemplate 053 * @see #setJndiEnvironment 054 * @see #setCache 055 * @see #setFallbackToDynamicDestination 056 */ 057public class JndiDestinationResolver extends JndiLocatorSupport implements CachingDestinationResolver { 058 059 private boolean cache = true; 060 061 private boolean fallbackToDynamicDestination = false; 062 063 private DestinationResolver dynamicDestinationResolver = new DynamicDestinationResolver(); 064 065 private final Map<String, Destination> destinationCache = new ConcurrentHashMap<>(16); 066 067 068 /** 069 * Set whether to cache resolved destinations. Default is "true". 070 * <p>This flag can be turned off to re-lookup a destination for each operation, 071 * which allows for hot restarting of destinations. This is mainly useful 072 * during development. 073 * <p>Note that dynamic queues and topics get cached by destination name. 074 * As a consequence, you need to use unique destination names across both 075 * queues and topics. 076 */ 077 public void setCache(boolean cache) { 078 this.cache = cache; 079 } 080 081 /** 082 * Set whether this resolver is supposed to create dynamic destinations 083 * if the destination name is not found in JNDI. Default is "false". 084 * <p>Turn this flag on to enable transparent fallback to dynamic destinations. 085 * @see #setDynamicDestinationResolver 086 */ 087 public void setFallbackToDynamicDestination(boolean fallbackToDynamicDestination) { 088 this.fallbackToDynamicDestination = fallbackToDynamicDestination; 089 } 090 091 /** 092 * Set the {@link DestinationResolver} to use when falling back to dynamic 093 * destinations. 094 * <p>The default is Spring's standard {@link DynamicDestinationResolver}. 095 * @see #setFallbackToDynamicDestination 096 * @see DynamicDestinationResolver 097 */ 098 public void setDynamicDestinationResolver(DestinationResolver dynamicDestinationResolver) { 099 this.dynamicDestinationResolver = dynamicDestinationResolver; 100 } 101 102 103 @Override 104 public Destination resolveDestinationName(@Nullable Session session, String destinationName, boolean pubSubDomain) 105 throws JMSException { 106 107 Assert.notNull(destinationName, "Destination name must not be null"); 108 Destination dest = this.destinationCache.get(destinationName); 109 if (dest != null) { 110 validateDestination(dest, destinationName, pubSubDomain); 111 } 112 else { 113 try { 114 dest = lookup(destinationName, Destination.class); 115 validateDestination(dest, destinationName, pubSubDomain); 116 } 117 catch (NamingException ex) { 118 if (logger.isDebugEnabled()) { 119 logger.debug("Destination [" + destinationName + "] not found in JNDI", ex); 120 } 121 if (this.fallbackToDynamicDestination) { 122 dest = this.dynamicDestinationResolver.resolveDestinationName(session, destinationName, pubSubDomain); 123 } 124 else { 125 throw new DestinationResolutionException( 126 "Destination [" + destinationName + "] not found in JNDI", ex); 127 } 128 } 129 if (this.cache) { 130 this.destinationCache.put(destinationName, dest); 131 } 132 } 133 return dest; 134 } 135 136 /** 137 * Validate the given Destination object, checking whether it matches 138 * the expected type. 139 * @param destination the Destination object to validate 140 * @param destinationName the name of the destination 141 * @param pubSubDomain {@code true} if a Topic is expected, 142 * {@code false} in case of a Queue 143 */ 144 protected void validateDestination(Destination destination, String destinationName, boolean pubSubDomain) { 145 Class<?> targetClass = Queue.class; 146 if (pubSubDomain) { 147 targetClass = Topic.class; 148 } 149 if (!targetClass.isInstance(destination)) { 150 throw new DestinationResolutionException( 151 "Destination [" + destinationName + "] is not of expected type [" + targetClass.getName() + "]"); 152 } 153 } 154 155 156 @Override 157 public void removeFromCache(String destinationName) { 158 this.destinationCache.remove(destinationName); 159 } 160 161 @Override 162 public void clearCache() { 163 this.destinationCache.clear(); 164 } 165 166}