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