001/*
002 * Copyright 2002-2019 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.interceptor;
018
019import java.beans.PropertyEditorSupport;
020
021import org.springframework.util.StringUtils;
022
023/**
024 * PropertyEditor for {@link TransactionAttribute} objects. Accepts a String of form
025 * <p>{@code PROPAGATION_NAME, ISOLATION_NAME, readOnly, timeout_NNNN,+Exception1,-Exception2}
026 * <p>where only propagation code is required. For example:
027 * <p>{@code PROPAGATION_MANDATORY, ISOLATION_DEFAULT}
028 *
029 * <p>The tokens can be in <strong>any</strong> order. Propagation and isolation codes
030 * must use the names of the constants in the TransactionDefinition class. Timeout values
031 * are in seconds. If no timeout is specified, the transaction manager will apply a default
032 * timeout specific to the particular transaction manager.
033 *
034 * <p>A "+" before an exception name substring indicates that transactions should commit
035 * even if this exception is thrown; a "-" that they should roll back.
036 *
037 * @author Rod Johnson
038 * @author Juergen Hoeller
039 * @since 24.04.2003
040 * @see org.springframework.transaction.TransactionDefinition
041 * @see org.springframework.core.Constants
042 */
043public class TransactionAttributeEditor extends PropertyEditorSupport {
044
045        /**
046         * Format is PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_NNNN,+Exception1,-Exception2.
047         * Null or the empty string means that the method is non transactional.
048         */
049        @Override
050        public void setAsText(String text) throws IllegalArgumentException {
051                if (StringUtils.hasLength(text)) {
052                        // tokenize it with ","
053                        String[] tokens = StringUtils.commaDelimitedListToStringArray(text);
054                        RuleBasedTransactionAttribute attr = new RuleBasedTransactionAttribute();
055                        for (String token : tokens) {
056                                // Trim leading and trailing whitespace.
057                                String trimmedToken = StringUtils.trimWhitespace(token.trim());
058                                // Check whether token contains illegal whitespace within text.
059                                if (StringUtils.containsWhitespace(trimmedToken)) {
060                                        throw new IllegalArgumentException(
061                                                        "Transaction attribute token contains illegal whitespace: [" + trimmedToken + "]");
062                                }
063                                // Check token type.
064                                if (trimmedToken.startsWith(RuleBasedTransactionAttribute.PREFIX_PROPAGATION)) {
065                                        attr.setPropagationBehaviorName(trimmedToken);
066                                }
067                                else if (trimmedToken.startsWith(RuleBasedTransactionAttribute.PREFIX_ISOLATION)) {
068                                        attr.setIsolationLevelName(trimmedToken);
069                                }
070                                else if (trimmedToken.startsWith(RuleBasedTransactionAttribute.PREFIX_TIMEOUT)) {
071                                        String value = trimmedToken.substring(DefaultTransactionAttribute.PREFIX_TIMEOUT.length());
072                                        attr.setTimeout(Integer.parseInt(value));
073                                }
074                                else if (trimmedToken.equals(RuleBasedTransactionAttribute.READ_ONLY_MARKER)) {
075                                        attr.setReadOnly(true);
076                                }
077                                else if (trimmedToken.startsWith(RuleBasedTransactionAttribute.PREFIX_COMMIT_RULE)) {
078                                        attr.getRollbackRules().add(new NoRollbackRuleAttribute(trimmedToken.substring(1)));
079                                }
080                                else if (trimmedToken.startsWith(RuleBasedTransactionAttribute.PREFIX_ROLLBACK_RULE)) {
081                                        attr.getRollbackRules().add(new RollbackRuleAttribute(trimmedToken.substring(1)));
082                                }
083                                else {
084                                        throw new IllegalArgumentException("Invalid transaction attribute token: [" + trimmedToken + "]");
085                                }
086                        }
087                        setValue(attr);
088                }
089                else {
090                        setValue(null);
091                }
092        }
093
094}