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