/* * Copyright 2002-2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.aop.target.dynamic; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.TargetSource; /** * Abstract {@link org.springframework.aop.TargetSource} implementation that * wraps a refreshable target object. Subclasses can determine whether a * refresh is required, and need to provide fresh target objects. * * <p>Implements the {@link Refreshable} interface in order to allow for * explicit control over the refresh status. * * @author Rod Johnson * @author Rob Harrop * @author Juergen Hoeller * @since 2.0 * @see #requiresRefresh() * @see #freshTarget() */ public abstract class AbstractRefreshableTargetSource implements TargetSource, Refreshable { /** Logger available to subclasses */ protected Log logger = LogFactory.getLog(getClass()); protected Object targetObject; private long refreshCheckDelay = -1; private long lastRefreshCheck = -1; private long lastRefreshTime = -1; private long refreshCount = 0; /** * Set the delay between refresh checks, in milliseconds. * Default is -1, indicating no refresh checks at all. * <p>Note that an actual refresh will only happen when * {@link #requiresRefresh()} returns <code>true</code>. */ public void setRefreshCheckDelay(long refreshCheckDelay) { this.refreshCheckDelay = refreshCheckDelay; } public synchronized Class getTargetClass() { if (this.targetObject == null) { refresh(); } return this.targetObject.getClass(); } /** * Not static. */ public boolean isStatic() { return false; } public final synchronized Object getTarget() { if ((refreshCheckDelayElapsed() && requiresRefresh()) || this.targetObject == null) { refresh(); } return this.targetObject; } /** * No need to release target. */ public void releaseTarget(Object object) { } public final synchronized void refresh() { logger.debug("Attempting to refresh target"); this.targetObject = freshTarget(); this.refreshCount++; this.lastRefreshTime = System.currentTimeMillis(); logger.debug("Target refreshed successfully"); } public synchronized long getRefreshCount() { return this.refreshCount; } public synchronized long getLastRefreshTime() { return this.lastRefreshTime; } private boolean refreshCheckDelayElapsed() { if (this.refreshCheckDelay < 0) { return false; } long currentTimeMillis = System.currentTimeMillis(); if (this.lastRefreshCheck < 0 || currentTimeMillis - this.lastRefreshCheck > this.refreshCheckDelay) { // Going to perform a refresh check - update the timestamp. this.lastRefreshCheck = currentTimeMillis; logger.debug("Refresh check delay elapsed - checking whether refresh is required"); return true; } return false; } /** * Determine whether a refresh is required. * Invoked for each refresh check, after the refresh check delay has elapsed. * <p>The default implementation always returns <code>true</code>, triggering * a refresh every time the delay has elapsed. To be overridden by subclasses * with an appropriate check of the underlying target resource. * @return whether a refresh is required */ protected boolean requiresRefresh() { return true; } /** * Obtain a fresh target object. * <p>Only invoked if a refresh check has found that a refresh is required * (that is, {@link #requiresRefresh()} has returned <code>true</code>). * @return the fresh target object */ protected abstract Object freshTarget(); }