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.transaction.support;
018
019import java.io.Serializable;
020
021import org.springframework.core.Constants;
022import org.springframework.lang.Nullable;
023import org.springframework.transaction.TransactionDefinition;
024
025/**
026 * Default implementation of the {@link TransactionDefinition} interface,
027 * offering bean-style configuration and sensible default values
028 * (PROPAGATION_REQUIRED, ISOLATION_DEFAULT, TIMEOUT_DEFAULT, readOnly=false).
029 *
030 * <p>Base class for both {@link TransactionTemplate} and
031 * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute}.
032 *
033 * @author Juergen Hoeller
034 * @since 08.05.2003
035 */
036@SuppressWarnings("serial")
037public class DefaultTransactionDefinition implements TransactionDefinition, Serializable {
038
039        /** Prefix for the propagation constants defined in TransactionDefinition. */
040        public static final String PREFIX_PROPAGATION = "PROPAGATION_";
041
042        /** Prefix for the isolation constants defined in TransactionDefinition. */
043        public static final String PREFIX_ISOLATION = "ISOLATION_";
044
045        /** Prefix for transaction timeout values in description strings. */
046        public static final String PREFIX_TIMEOUT = "timeout_";
047
048        /** Marker for read-only transactions in description strings. */
049        public static final String READ_ONLY_MARKER = "readOnly";
050
051
052        /** Constants instance for TransactionDefinition. */
053        static final Constants constants = new Constants(TransactionDefinition.class);
054
055        private int propagationBehavior = PROPAGATION_REQUIRED;
056
057        private int isolationLevel = ISOLATION_DEFAULT;
058
059        private int timeout = TIMEOUT_DEFAULT;
060
061        private boolean readOnly = false;
062
063        @Nullable
064        private String name;
065
066
067        /**
068         * Create a new DefaultTransactionDefinition, with default settings.
069         * Can be modified through bean property setters.
070         * @see #setPropagationBehavior
071         * @see #setIsolationLevel
072         * @see #setTimeout
073         * @see #setReadOnly
074         * @see #setName
075         */
076        public DefaultTransactionDefinition() {
077        }
078
079        /**
080         * Copy constructor. Definition can be modified through bean property setters.
081         * @see #setPropagationBehavior
082         * @see #setIsolationLevel
083         * @see #setTimeout
084         * @see #setReadOnly
085         * @see #setName
086         */
087        public DefaultTransactionDefinition(TransactionDefinition other) {
088                this.propagationBehavior = other.getPropagationBehavior();
089                this.isolationLevel = other.getIsolationLevel();
090                this.timeout = other.getTimeout();
091                this.readOnly = other.isReadOnly();
092                this.name = other.getName();
093        }
094
095        /**
096         * Create a new DefaultTransactionDefinition with the given
097         * propagation behavior. Can be modified through bean property setters.
098         * @param propagationBehavior one of the propagation constants in the
099         * TransactionDefinition interface
100         * @see #setIsolationLevel
101         * @see #setTimeout
102         * @see #setReadOnly
103         */
104        public DefaultTransactionDefinition(int propagationBehavior) {
105                this.propagationBehavior = propagationBehavior;
106        }
107
108
109        /**
110         * Set the propagation behavior by the name of the corresponding constant in
111         * TransactionDefinition, e.g. "PROPAGATION_REQUIRED".
112         * @param constantName name of the constant
113         * @throws IllegalArgumentException if the supplied value is not resolvable
114         * to one of the {@code PROPAGATION_} constants or is {@code null}
115         * @see #setPropagationBehavior
116         * @see #PROPAGATION_REQUIRED
117         */
118        public final void setPropagationBehaviorName(String constantName) throws IllegalArgumentException {
119                if (!constantName.startsWith(PREFIX_PROPAGATION)) {
120                        throw new IllegalArgumentException("Only propagation constants allowed");
121                }
122                setPropagationBehavior(constants.asNumber(constantName).intValue());
123        }
124
125        /**
126         * Set the propagation behavior. Must be one of the propagation constants
127         * in the TransactionDefinition interface. Default is PROPAGATION_REQUIRED.
128         * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or
129         * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started
130         * transactions. Consider switching the "validateExistingTransactions" flag to
131         * "true" on your transaction manager if you'd like isolation level declarations
132         * to get rejected when participating in an existing transaction with a different
133         * isolation level.
134         * <p>Note that a transaction manager that does not support custom isolation levels
135         * will throw an exception when given any other level than {@link #ISOLATION_DEFAULT}.
136         * @throws IllegalArgumentException if the supplied value is not one of the
137         * {@code PROPAGATION_} constants
138         * @see #PROPAGATION_REQUIRED
139         */
140        public final void setPropagationBehavior(int propagationBehavior) {
141                if (!constants.getValues(PREFIX_PROPAGATION).contains(propagationBehavior)) {
142                        throw new IllegalArgumentException("Only values of propagation constants allowed");
143                }
144                this.propagationBehavior = propagationBehavior;
145        }
146
147        @Override
148        public final int getPropagationBehavior() {
149                return this.propagationBehavior;
150        }
151
152        /**
153         * Set the isolation level by the name of the corresponding constant in
154         * TransactionDefinition, e.g. "ISOLATION_DEFAULT".
155         * @param constantName name of the constant
156         * @throws IllegalArgumentException if the supplied value is not resolvable
157         * to one of the {@code ISOLATION_} constants or is {@code null}
158         * @see #setIsolationLevel
159         * @see #ISOLATION_DEFAULT
160         */
161        public final void setIsolationLevelName(String constantName) throws IllegalArgumentException {
162                if (!constantName.startsWith(PREFIX_ISOLATION)) {
163                        throw new IllegalArgumentException("Only isolation constants allowed");
164                }
165                setIsolationLevel(constants.asNumber(constantName).intValue());
166        }
167
168        /**
169         * Set the isolation level. Must be one of the isolation constants
170         * in the TransactionDefinition interface. Default is ISOLATION_DEFAULT.
171         * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or
172         * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started
173         * transactions. Consider switching the "validateExistingTransactions" flag to
174         * "true" on your transaction manager if you'd like isolation level declarations
175         * to get rejected when participating in an existing transaction with a different
176         * isolation level.
177         * <p>Note that a transaction manager that does not support custom isolation levels
178         * will throw an exception when given any other level than {@link #ISOLATION_DEFAULT}.
179         * @throws IllegalArgumentException if the supplied value is not one of the
180         * {@code ISOLATION_} constants
181         * @see #ISOLATION_DEFAULT
182         */
183        public final void setIsolationLevel(int isolationLevel) {
184                if (!constants.getValues(PREFIX_ISOLATION).contains(isolationLevel)) {
185                        throw new IllegalArgumentException("Only values of isolation constants allowed");
186                }
187                this.isolationLevel = isolationLevel;
188        }
189
190        @Override
191        public final int getIsolationLevel() {
192                return this.isolationLevel;
193        }
194
195        /**
196         * Set the timeout to apply, as number of seconds.
197         * Default is TIMEOUT_DEFAULT (-1).
198         * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or
199         * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started
200         * transactions.
201         * <p>Note that a transaction manager that does not support timeouts will throw
202         * an exception when given any other timeout than {@link #TIMEOUT_DEFAULT}.
203         * @see #TIMEOUT_DEFAULT
204         */
205        public final void setTimeout(int timeout) {
206                if (timeout < TIMEOUT_DEFAULT) {
207                        throw new IllegalArgumentException("Timeout must be a positive integer or TIMEOUT_DEFAULT");
208                }
209                this.timeout = timeout;
210        }
211
212        @Override
213        public final int getTimeout() {
214                return this.timeout;
215        }
216
217        /**
218         * Set whether to optimize as read-only transaction.
219         * Default is "false".
220         * <p>The read-only flag applies to any transaction context, whether backed
221         * by an actual resource transaction ({@link #PROPAGATION_REQUIRED}/
222         * {@link #PROPAGATION_REQUIRES_NEW}) or operating non-transactionally at
223         * the resource level ({@link #PROPAGATION_SUPPORTS}). In the latter case,
224         * the flag will only apply to managed resources within the application,
225         * such as a Hibernate {@code Session}.
226         * <p>This just serves as a hint for the actual transaction subsystem;
227         * it will <i>not necessarily</i> cause failure of write access attempts.
228         * A transaction manager which cannot interpret the read-only hint will
229         * <i>not</i> throw an exception when asked for a read-only transaction.
230         */
231        public final void setReadOnly(boolean readOnly) {
232                this.readOnly = readOnly;
233        }
234
235        @Override
236        public final boolean isReadOnly() {
237                return this.readOnly;
238        }
239
240        /**
241         * Set the name of this transaction. Default is none.
242         * <p>This will be used as transaction name to be shown in a
243         * transaction monitor, if applicable (for example, WebLogic's).
244         */
245        public final void setName(String name) {
246                this.name = name;
247        }
248
249        @Override
250        @Nullable
251        public final String getName() {
252                return this.name;
253        }
254
255
256        /**
257         * This implementation compares the {@code toString()} results.
258         * @see #toString()
259         */
260        @Override
261        public boolean equals(@Nullable Object other) {
262                return (this == other || (other instanceof TransactionDefinition && toString().equals(other.toString())));
263        }
264
265        /**
266         * This implementation returns {@code toString()}'s hash code.
267         * @see #toString()
268         */
269        @Override
270        public int hashCode() {
271                return toString().hashCode();
272        }
273
274        /**
275         * Return an identifying description for this transaction definition.
276         * <p>The format matches the one used by
277         * {@link org.springframework.transaction.interceptor.TransactionAttributeEditor},
278         * to be able to feed {@code toString} results into bean properties of type
279         * {@link org.springframework.transaction.interceptor.TransactionAttribute}.
280         * <p>Has to be overridden in subclasses for correct {@code equals}
281         * and {@code hashCode} behavior. Alternatively, {@link #equals}
282         * and {@link #hashCode} can be overridden themselves.
283         * @see #getDefinitionDescription()
284         * @see org.springframework.transaction.interceptor.TransactionAttributeEditor
285         */
286        @Override
287        public String toString() {
288                return getDefinitionDescription().toString();
289        }
290
291        /**
292         * Return an identifying description for this transaction definition.
293         * <p>Available to subclasses, for inclusion in their {@code toString()} result.
294         */
295        protected final StringBuilder getDefinitionDescription() {
296                StringBuilder result = new StringBuilder();
297                result.append(constants.toCode(this.propagationBehavior, PREFIX_PROPAGATION));
298                result.append(',');
299                result.append(constants.toCode(this.isolationLevel, PREFIX_ISOLATION));
300                if (this.timeout != TIMEOUT_DEFAULT) {
301                        result.append(',');
302                        result.append(PREFIX_TIMEOUT).append(this.timeout);
303                }
304                if (this.readOnly) {
305                        result.append(',');
306                        result.append(READ_ONLY_MARKER);
307                }
308                return result;
309        }
310
311}