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.transaction.support;
018
019import java.util.HashMap;
020import java.util.LinkedHashMap;
021import java.util.Map;
022
023import org.springframework.beans.factory.ObjectFactory;
024import org.springframework.beans.factory.config.Scope;
025
026/**
027 * A simple transaction-backed {@link Scope} implementation, delegating to
028 * {@link TransactionSynchronizationManager}'s resource binding mechanism.
029 *
030 * <p><b>NOTE:</b> Like {@link org.springframework.context.support.SimpleThreadScope},
031 * this transaction scope is not registered by default in common contexts. Instead,
032 * you need to explicitly assign it to a scope key in your setup, either through
033 * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope}
034 * or through a {@link org.springframework.beans.factory.config.CustomScopeConfigurer} bean.
035 *
036 * @author Juergen Hoeller
037 * @since 4.2
038 * @see org.springframework.context.support.SimpleThreadScope
039 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope
040 * @see org.springframework.beans.factory.config.CustomScopeConfigurer
041 */
042public class SimpleTransactionScope implements Scope {
043
044        @Override
045        public Object get(String name, ObjectFactory<?> objectFactory) {
046                ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) TransactionSynchronizationManager.getResource(this);
047                if (scopedObjects == null) {
048                        scopedObjects = new ScopedObjectsHolder();
049                        TransactionSynchronizationManager.registerSynchronization(new CleanupSynchronization(scopedObjects));
050                        TransactionSynchronizationManager.bindResource(this, scopedObjects);
051                }
052                Object scopedObject = scopedObjects.scopedInstances.get(name);
053                if (scopedObject == null) {
054                        scopedObject = objectFactory.getObject();
055                        scopedObjects.scopedInstances.put(name, scopedObject);
056                }
057                return scopedObject;
058        }
059
060        @Override
061        public Object remove(String name) {
062                ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) TransactionSynchronizationManager.getResource(this);
063                if (scopedObjects != null) {
064                        scopedObjects.destructionCallbacks.remove(name);
065                        return scopedObjects.scopedInstances.remove(name);
066                }
067                else {
068                        return null;
069                }
070        }
071
072        @Override
073        public void registerDestructionCallback(String name, Runnable callback) {
074                ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) TransactionSynchronizationManager.getResource(this);
075                if (scopedObjects != null) {
076                        scopedObjects.destructionCallbacks.put(name, callback);
077                }
078        }
079
080        @Override
081        public Object resolveContextualObject(String key) {
082                return null;
083        }
084
085        @Override
086        public String getConversationId() {
087                return TransactionSynchronizationManager.getCurrentTransactionName();
088        }
089
090
091        static class ScopedObjectsHolder {
092
093                final Map<String, Object> scopedInstances = new HashMap<String, Object>();
094
095                final Map<String, Runnable> destructionCallbacks = new LinkedHashMap<String, Runnable>();
096        }
097
098
099        private class CleanupSynchronization extends TransactionSynchronizationAdapter {
100
101                private final ScopedObjectsHolder scopedObjects;
102
103                public CleanupSynchronization(ScopedObjectsHolder scopedObjects) {
104                        this.scopedObjects = scopedObjects;
105                }
106
107                @Override
108                public void suspend() {
109                        TransactionSynchronizationManager.unbindResource(SimpleTransactionScope.this);
110                }
111
112                @Override
113                public void resume() {
114                        TransactionSynchronizationManager.bindResource(SimpleTransactionScope.this, this.scopedObjects);
115                }
116
117                @Override
118                public void afterCompletion(int status) {
119                        TransactionSynchronizationManager.unbindResourceIfPossible(SimpleTransactionScope.this);
120                        for (Runnable callback : this.scopedObjects.destructionCallbacks.values()) {
121                                callback.run();
122                        }
123                        this.scopedObjects.destructionCallbacks.clear();
124                        this.scopedObjects.scopedInstances.clear();
125                }
126        }
127
128}