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 * &lt;bean id="queueConnectionFactoryTarget" class="org.springframework.jndi.JndiObjectTargetSource"&gt;
037 *   &lt;property name="jndiName" value="JmsQueueConnectionFactory"/&gt;
038 *   &lt;property name="lookupOnStartup" value="false"/&gt;
039 * &lt;/bean&gt;
040 *
041 * &lt;bean id="queueConnectionFactory" class="org.springframework.aop.framework.ProxyFactoryBean"&gt;
042 *   &lt;property name="proxyInterfaces" value="javax.jms.QueueConnectionFactory"/&gt;
043 *   &lt;property name="targetSource" ref="queueConnectionFactoryTarget"/&gt;
044 * &lt;/bean&gt;</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}