diff --git a/src/main/java/org/mybatis/guice/AbstractMyBatisModule.java b/src/main/java/org/mybatis/guice/AbstractMyBatisModule.java index 57747957..06265f16 100644 --- a/src/main/java/org/mybatis/guice/AbstractMyBatisModule.java +++ b/src/main/java/org/mybatis/guice/AbstractMyBatisModule.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2022 the original author or authors. + * Copyright 2009-2023 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. @@ -20,6 +20,7 @@ import static com.google.inject.matcher.Matchers.not; import static com.google.inject.name.Names.named; import static com.google.inject.util.Providers.guicify; +import static org.mybatis.guice.Preconditions.checkArgument; import com.google.inject.AbstractModule; import com.google.inject.Binder; @@ -27,7 +28,9 @@ import com.google.inject.matcher.AbstractMatcher; import java.lang.reflect.Method; +import java.util.Set; +import org.apache.ibatis.io.ResolverUtil; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionManager; import org.mybatis.guice.mappers.MapperProvider; @@ -55,6 +58,34 @@ public boolean matches(Method method) { private ClassLoader driverClassLoader = getDefaultClassLoader(); + /** + * Return a set of all classes contained in the given package. + * + * @param packageName + * the package has to be analyzed. + * + * @return a set of all classes contained in the given package. + */ + protected static Set> getClasses(String packageName) { + return AbstractMyBatisModule.getClasses(new ResolverUtil.IsA(Object.class), packageName); + } + + /** + * Return a set of all classes contained in the given package that match with the given test requirement. + * + * @param test + * the class filter on the given package. + * @param packageName + * the package has to be analyzed. + * + * @return a set of all classes contained in the given package. + */ + protected static Set> getClasses(ResolverUtil.Test test, String packageName) { + checkArgument(test != null, "Parameter 'test' must not be null"); + checkArgument(packageName != null, "Parameter 'packageName' must not be null"); + return new ResolverUtil().find(test, packageName).getClasses(); + } + @Override protected final void configure() { try { diff --git a/src/main/java/org/mybatis/guice/MyBatisModule.java b/src/main/java/org/mybatis/guice/MyBatisModule.java index a146f84b..acce45b8 100644 --- a/src/main/java/org/mybatis/guice/MyBatisModule.java +++ b/src/main/java/org/mybatis/guice/MyBatisModule.java @@ -689,32 +689,4 @@ protected final void addMapperClasses(final String packageName) { protected final void addMapperClasses(final String packageName, final ResolverUtil.Test test) { addMapperClasses(getClasses(test, packageName)); } - - /** - * Return a set of all classes contained in the given package. - * - * @param packageName - * the package has to be analyzed. - * - * @return a set of all classes contained in the given package. - */ - private static Set> getClasses(String packageName) { - return getClasses(new ResolverUtil.IsA(Object.class), packageName); - } - - /** - * Return a set of all classes contained in the given package that match with the given test requirement. - * - * @param test - * the class filter on the given package. - * @param packageName - * the package has to be analyzed. - * - * @return a set of all classes contained in the given package. - */ - private static Set> getClasses(ResolverUtil.Test test, String packageName) { - checkArgument(test != null, "Parameter 'test' must not be null"); - checkArgument(packageName != null, "Parameter 'packageName' must not be null"); - return new ResolverUtil().find(test, packageName).getClasses(); - } } diff --git a/src/main/java/org/mybatis/guice/datasource/helper/JdbcUrlAntFormatter.java b/src/main/java/org/mybatis/guice/datasource/helper/JdbcUrlAntFormatter.java index 17389cba..4c6e6eeb 100644 --- a/src/main/java/org/mybatis/guice/datasource/helper/JdbcUrlAntFormatter.java +++ b/src/main/java/org/mybatis/guice/datasource/helper/JdbcUrlAntFormatter.java @@ -36,48 +36,87 @@ public final class JdbcUrlAntFormatter implements Provider { private final List resolvers = new ArrayList(); /** - * Instantiates a new jdbc url ant formatter. + * Constructs a new JdbcUrlAntFormatter based on the provided pattern. * * @param pattern - * the pattern + * the pattern for URL formatting */ public JdbcUrlAntFormatter(final String pattern) { - int prev = 0; - int pos; - while ((pos = pattern.indexOf(VAR_BEGIN, prev)) >= 0) { - if (pos > 0) { - appenders.add(Providers.of(pattern.substring(prev, pos))); - } - if (pos == pattern.length() - 1) { - appenders.add(Providers.of(VAR_BEGIN)); - prev = pos + 1; - } else if (pattern.charAt(pos + 1) != '{') { - if (pattern.charAt(pos + 1) == '$') { - appenders.add(Providers.of(VAR_BEGIN)); - prev = pos + 2; - } else { - appenders.add(Providers.of(pattern.substring(pos, pos + 2))); - prev = pos + 2; - } - } else { - int endName = pattern.indexOf('}', pos); - if (endName < 0) { - throw new IllegalArgumentException("Syntax error in property: " + pattern); - } - StringTokenizer keyTokenizer = new StringTokenizer(pattern.substring(pos + 2, endName), PIPE_SEPARATOR); - String key = keyTokenizer.nextToken(); - String defaultValue = null; - if (keyTokenizer.hasMoreTokens()) { - defaultValue = keyTokenizer.nextToken(); - } - KeyResolver resolver = new KeyResolver(key, defaultValue); - appenders.add(resolver); - resolvers.add(resolver); - prev = endName + 1; - } + initializeAppender(pattern); + } + + /** + * Initializes the appenders based on the given pattern. + * + * @param pattern + * the pattern for URL formatting + */ + private void initializeAppender(String pattern) { + int previousIndex = 0; + int currentIndex; + while ((currentIndex = pattern.indexOf(VAR_BEGIN, previousIndex)) >= 0) { + processPatternSubstring(pattern, previousIndex, currentIndex); + previousIndex = updatePrevPosition(pattern, currentIndex); + } + appendRemainingPatternSubstring(pattern, previousIndex); + } + + // Processes the substring of the pattern based on the currentIndex and previousIndex + private void processPatternSubstring(String pattern, int previousIndex, int currentIndex) { + if (currentIndex > 0) { + appenders.add(Providers.of(pattern.substring(previousIndex, currentIndex))); + } + + if (currentIndex == pattern.length() - 1) { + appenders.add(Providers.of(VAR_BEGIN)); + } else if (pattern.charAt(currentIndex + 1) != '{') { + handleNonCurlyBracePattern(pattern, currentIndex); + } else { + handleCurlyBracePattern(pattern, currentIndex); + } + } + + // Handles patterns without curly braces + private void handleNonCurlyBracePattern(String pattern, int currentIndex) { + if (pattern.charAt(currentIndex + 1) == '$') { + appenders.add(Providers.of(VAR_BEGIN)); + } else { + appenders.add(Providers.of(pattern.substring(currentIndex, currentIndex + 2))); + } + } + + // Handles patterns with curly braces + private void handleCurlyBracePattern(String pattern, int currentIndex) { + int endName = pattern.indexOf('}', currentIndex); + if (endName < 0) { + throw new IllegalArgumentException("Syntax error in property: " + pattern); + } + processKeyResolver(pattern, currentIndex, endName); + } + + // Method to append KeyResolver based on the variable + private void processKeyResolver(String pattern, int startPos, int endPos) { + StringTokenizer keyTokenizer = new StringTokenizer(pattern.substring(startPos + 2, endPos), PIPE_SEPARATOR); + String key = keyTokenizer.nextToken(); + String defaultValue = keyTokenizer.hasMoreTokens() ? keyTokenizer.nextToken() : null; + KeyResolver resolver = new KeyResolver(key, defaultValue); + appenders.add(resolver); + resolvers.add(resolver); + } + + // Updates the previous position based on the current index and pattern + private int updatePrevPosition(String pattern, int currentIndex) { + if (pattern.charAt(currentIndex + 1) == '{') { + return pattern.indexOf('}', currentIndex) + 1; + } else { + return currentIndex + (pattern.charAt(currentIndex + 1) == '$' ? 2 : 1); } - if (prev < pattern.length()) { - appenders.add(Providers.of(pattern.substring(prev))); + } + + // Appends the remaining substring of the pattern + private void appendRemainingPatternSubstring(String pattern, int previousIndex) { + if (previousIndex < pattern.length()) { + appenders.add(Providers.of(pattern.substring(previousIndex))); } } diff --git a/src/main/java/org/mybatis/guice/transactional/MandatoryTransactionAttributeStrategy.java b/src/main/java/org/mybatis/guice/transactional/MandatoryTransactionAttributeStrategy.java new file mode 100644 index 00000000..51cb9d36 --- /dev/null +++ b/src/main/java/org/mybatis/guice/transactional/MandatoryTransactionAttributeStrategy.java @@ -0,0 +1,23 @@ +/* + * Copyright 2009-2023 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 + * + * https://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.mybatis.guice.transactional; + +public class MandatoryTransactionAttributeStrategy implements TransactionAttributeStrategy { + @Override + public TransactionAttribute getTransactionAttribute() { + return TransactionAttribute.MANDATORY; + } +} diff --git a/src/main/java/org/mybatis/guice/transactional/NeverTransactionAttributeStrategy.java b/src/main/java/org/mybatis/guice/transactional/NeverTransactionAttributeStrategy.java new file mode 100644 index 00000000..6191216a --- /dev/null +++ b/src/main/java/org/mybatis/guice/transactional/NeverTransactionAttributeStrategy.java @@ -0,0 +1,23 @@ +/* + * Copyright 2009-2023 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 + * + * https://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.mybatis.guice.transactional; + +public class NeverTransactionAttributeStrategy implements TransactionAttributeStrategy { + @Override + public TransactionAttribute getTransactionAttribute() { + return TransactionAttribute.NEVER; + } +} diff --git a/src/main/java/org/mybatis/guice/transactional/RequiredTransactionAttributeStrategy.java b/src/main/java/org/mybatis/guice/transactional/RequiredTransactionAttributeStrategy.java new file mode 100644 index 00000000..5749f4f0 --- /dev/null +++ b/src/main/java/org/mybatis/guice/transactional/RequiredTransactionAttributeStrategy.java @@ -0,0 +1,23 @@ +/* + * Copyright 2009-2023 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 + * + * https://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.mybatis.guice.transactional; + +public class RequiredTransactionAttributeStrategy implements TransactionAttributeStrategy { + @Override + public TransactionAttribute getTransactionAttribute() { + return TransactionAttribute.REQUIRED; + } +} diff --git a/src/main/java/org/mybatis/guice/transactional/RequiresNewTransactionAttributeStrategy.java b/src/main/java/org/mybatis/guice/transactional/RequiresNewTransactionAttributeStrategy.java new file mode 100644 index 00000000..ca921e48 --- /dev/null +++ b/src/main/java/org/mybatis/guice/transactional/RequiresNewTransactionAttributeStrategy.java @@ -0,0 +1,23 @@ +/* + * Copyright 2009-2023 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 + * + * https://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.mybatis.guice.transactional; + +public class RequiresNewTransactionAttributeStrategy implements TransactionAttributeStrategy { + @Override + public TransactionAttribute getTransactionAttribute() { + return TransactionAttribute.REQUIRESNEW; + } +} diff --git a/src/main/java/org/mybatis/guice/transactional/SupportsTransactionAttributeStrategy.java b/src/main/java/org/mybatis/guice/transactional/SupportsTransactionAttributeStrategy.java new file mode 100644 index 00000000..41459114 --- /dev/null +++ b/src/main/java/org/mybatis/guice/transactional/SupportsTransactionAttributeStrategy.java @@ -0,0 +1,23 @@ +/* + * Copyright 2009-2023 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 + * + * https://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.mybatis.guice.transactional; + +public class SupportsTransactionAttributeStrategy implements TransactionAttributeStrategy { + @Override + public TransactionAttribute getTransactionAttribute() { + return TransactionAttribute.SUPPORTS; + } +} diff --git a/src/main/java/org/mybatis/guice/transactional/TransactionAttributeStrategy.java b/src/main/java/org/mybatis/guice/transactional/TransactionAttributeStrategy.java new file mode 100644 index 00000000..a3af9de0 --- /dev/null +++ b/src/main/java/org/mybatis/guice/transactional/TransactionAttributeStrategy.java @@ -0,0 +1,20 @@ +/* + * Copyright 2009-2023 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 + * + * https://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.mybatis.guice.transactional; + +public interface TransactionAttributeStrategy { + TransactionAttribute getTransactionAttribute(); +} diff --git a/src/main/java/org/mybatis/guice/transactional/TxTransactionalMethodInterceptor.java b/src/main/java/org/mybatis/guice/transactional/TxTransactionalMethodInterceptor.java index 85862ddb..ee055629 100644 --- a/src/main/java/org/mybatis/guice/transactional/TxTransactionalMethodInterceptor.java +++ b/src/main/java/org/mybatis/guice/transactional/TxTransactionalMethodInterceptor.java @@ -24,6 +24,8 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; import javax.transaction.xa.XAResource; @@ -48,7 +50,14 @@ public class TxTransactionalMethodInterceptor implements MethodInterceptor { @Inject private Provider xaResourceProvider; + private Map strategies = new HashMap<>(); + public TxTransactionalMethodInterceptor() { + strategies.put(TxType.REQUIRED, new RequiredTransactionAttributeStrategy()); + strategies.put(TxType.REQUIRES_NEW, new RequiresNewTransactionAttributeStrategy()); + strategies.put(TxType.MANDATORY, new MandatoryTransactionAttributeStrategy()); + strategies.put(TxType.SUPPORTS, new SupportsTransactionAttributeStrategy()); + strategies.put(TxType.NEVER, new NeverTransactionAttributeStrategy()); } private boolean isApplicationExceptionAvailable() { @@ -94,18 +103,9 @@ public Object invoke(MethodInvocation invocation) throws Throwable { if (manager != null) { TxType txType = transactional.value(); - if (TxType.REQUIRED.equals(txType)) { - attribute = TransactionAttribute.REQUIRED; - } else if (TxType.REQUIRES_NEW.equals(txType)) { - attribute = TransactionAttribute.REQUIRESNEW; - } else if (TxType.MANDATORY.equals(txType)) { - attribute = TransactionAttribute.MANDATORY; - } else if (TxType.SUPPORTS.equals(txType)) { - attribute = TransactionAttribute.SUPPORTS; - } else if (TxType.NOT_SUPPORTED.equals(txType)) { - attribute = null; // FIXME add implementation - } else if (TxType.NEVER.equals(txType)) { - attribute = TransactionAttribute.NEVER; + TransactionAttributeStrategy strategy = strategies.get(txType); + if (strategy != null) { + attribute = strategy.getTransactionAttribute(); } } @@ -151,8 +151,15 @@ public Object invoke(MethodInvocation invocation) throws Throwable { if (isApplicationExceptionAvailable()) { ApplicationException ae = t.getClass().getAnnotation(ApplicationException.class); ApplicationException parentAe = findAnnotation(t.getClass().getSuperclass(), ApplicationException.class); - if ((ae == null && parentAe == null) || (ae != null && ae.rollback()) - || (parentAe != null && (!parentAe.inherited() || parentAe.rollback()))) { + boolean bothAEsNull = (ae == null && parentAe == null); + boolean aeNotNullAndRollback = (ae != null && ae.rollback()); + boolean parentAeRollbackConditions = false; + if (parentAe != null) { + parentAeRollbackConditions = (!parentAe.inherited() || parentAe.rollback()); + } + boolean shouldTriggerRollback = (bothAEsNull || aeNotNullAndRollback || parentAeRollbackConditions); + // Combine the simplified conditions + if (shouldTriggerRollback) { manager.setRollbackOnly(); } } else {