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