001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.aop.target;
018:
019: import java.util.Collections;
020: import java.util.HashSet;
021: import java.util.Iterator;
022: import java.util.Set;
023:
024: import org.springframework.aop.IntroductionAdvisor;
025: import org.springframework.aop.support.DefaultIntroductionAdvisor;
026: import org.springframework.aop.support.DelegatingIntroductionInterceptor;
027: import org.springframework.beans.BeansException;
028: import org.springframework.beans.factory.DisposableBean;
029:
030: /**
031: * Alternative to an object pool. This TargetSource uses a threading model in which
032: * every thread has its own copy of the target. There's no contention for targets.
033: * Target object creation is kept to a minimum on the running server.
034: *
035: * <p>Application code is written as to a normal pool; callers can't assume they
036: * will be dealing with the same instance in invocations in different threads.
037: * However, state can be relied on during the operations of a single thread:
038: * for example, if one caller makes repeated calls on the AOP proxy.
039: *
040: * <p>Cleanup of thread-bound objects is performed on BeanFactory destruction,
041: * calling their <code>DisposableBean.destroy()</code> method if available.
042: * Be aware that many thread-bound objects can be around until the application
043: * actually shuts down.
044: *
045: * @author Rod Johnson
046: * @author Juergen Hoeller
047: * @author Rob Harrop
048: * @see ThreadLocalTargetSourceStats
049: * @see org.springframework.beans.factory.DisposableBean#destroy()
050: */
051: public class ThreadLocalTargetSource extends
052: AbstractPrototypeBasedTargetSource implements
053: ThreadLocalTargetSourceStats, DisposableBean {
054:
055: /**
056: * ThreadLocal holding the target associated with the current
057: * thread. Unlike most ThreadLocals, which are static, this variable
058: * is meant to be per thread per instance of the ThreadLocalTargetSource class.
059: */
060: private final ThreadLocal targetInThread = new ThreadLocal();
061:
062: /**
063: * Set of managed targets, enabling us to keep track of the targets we've created.
064: */
065: private final Set targetSet = Collections
066: .synchronizedSet(new HashSet());
067:
068: private int invocationCount;
069:
070: private int hitCount;
071:
072: /**
073: * Implementation of abstract getTarget() method.
074: * We look for a target held in a ThreadLocal. If we don't find one,
075: * we create one and bind it to the thread. No synchronization is required.
076: */
077: public Object getTarget() throws BeansException {
078: ++this .invocationCount;
079: Object target = this .targetInThread.get();
080: if (target == null) {
081: if (logger.isDebugEnabled()) {
082: logger.debug("No target for prototype '"
083: + getTargetBeanName() + "' bound to thread: "
084: + "creating one and binding it to thread '"
085: + Thread.currentThread().getName() + "'");
086: }
087: // Associate target with ThreadLocal.
088: target = newPrototypeInstance();
089: this .targetInThread.set(target);
090: this .targetSet.add(target);
091: } else {
092: ++this .hitCount;
093: }
094: return target;
095: }
096:
097: /**
098: * Dispose of targets if necessary; clear ThreadLocal.
099: * @see #destroyPrototypeInstance
100: */
101: public void destroy() {
102: logger.debug("Destroying ThreadLocalTargetSource bindings");
103: synchronized (this .targetSet) {
104: for (Iterator it = this .targetSet.iterator(); it.hasNext();) {
105: destroyPrototypeInstance(it.next());
106: }
107: this .targetSet.clear();
108: }
109: // Clear ThreadLocal, just in case.
110: this .targetInThread.set(null);
111: }
112:
113: public int getInvocationCount() {
114: return this .invocationCount;
115: }
116:
117: public int getHitCount() {
118: return this .hitCount;
119: }
120:
121: public int getObjectCount() {
122: return this .targetSet.size();
123: }
124:
125: /**
126: * Return an introduction advisor mixin that allows the AOP proxy to be
127: * cast to ThreadLocalInvokerStats.
128: */
129: public IntroductionAdvisor getStatsMixin() {
130: DelegatingIntroductionInterceptor dii = new DelegatingIntroductionInterceptor(
131: this );
132: return new DefaultIntroductionAdvisor(dii,
133: ThreadLocalTargetSourceStats.class);
134: }
135:
136: }
|