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.aop.target.dynamic;
018
019import org.apache.commons.logging.Log;
020import org.apache.commons.logging.LogFactory;
021
022import org.springframework.aop.TargetSource;
023
024/**
025 * Abstract {@link org.springframework.aop.TargetSource} implementation that
026 * wraps a refreshable target object. Subclasses can determine whether a
027 * refresh is required, and need to provide fresh target objects.
028 *
029 * <p>Implements the {@link Refreshable} interface in order to allow for
030 * explicit control over the refresh status.
031 *
032 * @author Rod Johnson
033 * @author Rob Harrop
034 * @author Juergen Hoeller
035 * @since 2.0
036 * @see #requiresRefresh()
037 * @see #freshTarget()
038 */
039public abstract class AbstractRefreshableTargetSource implements TargetSource, Refreshable {
040
041        /** Logger available to subclasses */
042        protected final Log logger = LogFactory.getLog(getClass());
043
044        protected Object targetObject;
045
046        private long refreshCheckDelay = -1;
047
048        private long lastRefreshCheck = -1;
049
050        private long lastRefreshTime = -1;
051
052        private long refreshCount = 0;
053
054
055        /**
056         * Set the delay between refresh checks, in milliseconds.
057         * Default is -1, indicating no refresh checks at all.
058         * <p>Note that an actual refresh will only happen when
059         * {@link #requiresRefresh()} returns {@code true}.
060         */
061        public void setRefreshCheckDelay(long refreshCheckDelay) {
062                this.refreshCheckDelay = refreshCheckDelay;
063        }
064
065
066        @Override
067        public synchronized Class<?> getTargetClass() {
068                if (this.targetObject == null) {
069                        refresh();
070                }
071                return this.targetObject.getClass();
072        }
073
074        /**
075         * Not static.
076         */
077        @Override
078        public boolean isStatic() {
079                return false;
080        }
081
082        @Override
083        public final synchronized Object getTarget() {
084                if ((refreshCheckDelayElapsed() && requiresRefresh()) || this.targetObject == null) {
085                        refresh();
086                }
087                return this.targetObject;
088        }
089
090        /**
091         * No need to release target.
092         */
093        @Override
094        public void releaseTarget(Object object) {
095        }
096
097
098        @Override
099        public final synchronized void refresh() {
100                logger.debug("Attempting to refresh target");
101
102                this.targetObject = freshTarget();
103                this.refreshCount++;
104                this.lastRefreshTime = System.currentTimeMillis();
105
106                logger.debug("Target refreshed successfully");
107        }
108
109        @Override
110        public synchronized long getRefreshCount() {
111                return this.refreshCount;
112        }
113
114        @Override
115        public synchronized long getLastRefreshTime() {
116                return this.lastRefreshTime;
117        }
118
119
120        private boolean refreshCheckDelayElapsed() {
121                if (this.refreshCheckDelay < 0) {
122                        return false;
123                }
124
125                long currentTimeMillis = System.currentTimeMillis();
126
127                if (this.lastRefreshCheck < 0 || currentTimeMillis - this.lastRefreshCheck > this.refreshCheckDelay) {
128                        // Going to perform a refresh check - update the timestamp.
129                        this.lastRefreshCheck = currentTimeMillis;
130                        logger.debug("Refresh check delay elapsed - checking whether refresh is required");
131                        return true;
132                }
133
134                return false;
135        }
136
137
138        /**
139         * Determine whether a refresh is required.
140         * Invoked for each refresh check, after the refresh check delay has elapsed.
141         * <p>The default implementation always returns {@code true}, triggering
142         * a refresh every time the delay has elapsed. To be overridden by subclasses
143         * with an appropriate check of the underlying target resource.
144         * @return whether a refresh is required
145         */
146        protected boolean requiresRefresh() {
147                return true;
148        }
149
150        /**
151         * Obtain a fresh target object.
152         * <p>Only invoked if a refresh check has found that a refresh is required
153         * (that is, {@link #requiresRefresh()} has returned {@code true}).
154         * @return the fresh target object
155         */
156        protected abstract Object freshTarget();
157
158}