001/* 002 * Copyright 2002-2017 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.jndi; 018 019import javax.naming.NamingException; 020 021import org.springframework.aop.TargetSource; 022import org.springframework.lang.Nullable; 023 024/** 025 * AOP {@link org.springframework.aop.TargetSource} that provides 026 * configurable JNDI lookups for {@code getTarget()} calls. 027 * 028 * <p>Can be used as alternative to {@link JndiObjectFactoryBean}, to allow for 029 * relocating a JNDI object lazily or for each operation (see "lookupOnStartup" 030 * and "cache" properties). This is particularly useful during development, as it 031 * allows for hot restarting of the JNDI server (for example, a remote JMS server). 032 * 033 * <p>Example: 034 * 035 * <pre class="code"> 036 * <bean id="queueConnectionFactoryTarget" class="org.springframework.jndi.JndiObjectTargetSource"> 037 * <property name="jndiName" value="JmsQueueConnectionFactory"/> 038 * <property name="lookupOnStartup" value="false"/> 039 * </bean> 040 * 041 * <bean id="queueConnectionFactory" class="org.springframework.aop.framework.ProxyFactoryBean"> 042 * <property name="proxyInterfaces" value="javax.jms.QueueConnectionFactory"/> 043 * <property name="targetSource" ref="queueConnectionFactoryTarget"/> 044 * </bean></pre> 045 * 046 * A {@code createQueueConnection} call on the "queueConnectionFactory" proxy will 047 * cause a lazy JNDI lookup for "JmsQueueConnectionFactory" and a subsequent delegating 048 * call to the retrieved QueueConnectionFactory's {@code createQueueConnection}. 049 * 050 * <p><b>Alternatively, use a {@link JndiObjectFactoryBean} with a "proxyInterface".</b> 051 * "lookupOnStartup" and "cache" can then be specified on the JndiObjectFactoryBean, 052 * creating a JndiObjectTargetSource underneath (instead of defining separate 053 * ProxyFactoryBean and JndiObjectTargetSource beans). 054 * 055 * @author Juergen Hoeller 056 * @since 1.1 057 * @see #setLookupOnStartup 058 * @see #setCache 059 * @see org.springframework.aop.framework.ProxyFactoryBean#setTargetSource 060 * @see JndiObjectFactoryBean#setProxyInterface 061 */ 062public class JndiObjectTargetSource extends JndiObjectLocator implements TargetSource { 063 064 private boolean lookupOnStartup = true; 065 066 private boolean cache = true; 067 068 @Nullable 069 private Object cachedObject; 070 071 @Nullable 072 private Class<?> targetClass; 073 074 075 /** 076 * Set whether to look up the JNDI object on startup. Default is "true". 077 * <p>Can be turned off to allow for late availability of the JNDI object. 078 * In this case, the JNDI object will be fetched on first access. 079 * @see #setCache 080 */ 081 public void setLookupOnStartup(boolean lookupOnStartup) { 082 this.lookupOnStartup = lookupOnStartup; 083 } 084 085 /** 086 * Set whether to cache the JNDI object once it has been located. 087 * Default is "true". 088 * <p>Can be turned off to allow for hot redeployment of JNDI objects. 089 * In this case, the JNDI object will be fetched for each invocation. 090 * @see #setLookupOnStartup 091 */ 092 public void setCache(boolean cache) { 093 this.cache = cache; 094 } 095 096 @Override 097 public void afterPropertiesSet() throws NamingException { 098 super.afterPropertiesSet(); 099 if (this.lookupOnStartup) { 100 Object object = lookup(); 101 if (this.cache) { 102 this.cachedObject = object; 103 } 104 else { 105 this.targetClass = object.getClass(); 106 } 107 } 108 } 109 110 111 @Override 112 @Nullable 113 public Class<?> getTargetClass() { 114 if (this.cachedObject != null) { 115 return this.cachedObject.getClass(); 116 } 117 else if (this.targetClass != null) { 118 return this.targetClass; 119 } 120 else { 121 return getExpectedType(); 122 } 123 } 124 125 @Override 126 public boolean isStatic() { 127 return (this.cachedObject != null); 128 } 129 130 @Override 131 @Nullable 132 public Object getTarget() { 133 try { 134 if (this.lookupOnStartup || !this.cache) { 135 return (this.cachedObject != null ? this.cachedObject : lookup()); 136 } 137 else { 138 synchronized (this) { 139 if (this.cachedObject == null) { 140 this.cachedObject = lookup(); 141 } 142 return this.cachedObject; 143 } 144 } 145 } 146 catch (NamingException ex) { 147 throw new JndiLookupFailureException("JndiObjectTargetSource failed to obtain new target object", ex); 148 } 149 } 150 151 @Override 152 public void releaseTarget(Object target) { 153 } 154 155}