Repository: deltaspike Updated Branches: refs/heads/master 303507d6b -> db37a83a2
DELTASPIKE-420 Repository integration with TransactionStrategy Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/db37a83a Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/db37a83a Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/db37a83a Branch: refs/heads/master Commit: db37a83a2cdd44cde147e6f9926c63e80422c5c6 Parents: 303507d Author: Thomas Hug <[email protected]> Authored: Wed Feb 19 08:34:58 2014 +0100 Committer: Thomas Hug <[email protected]> Committed: Thu Feb 27 09:49:48 2014 +0100 ---------------------------------------------------------------------- deltaspike/modules/data/impl/pom.xml | 12 ++ .../data/impl/builder/DelegateQueryBuilder.java | 21 +--- .../impl/handler/CdiQueryInvocationContext.java | 5 + .../impl/handler/EntityRepositoryHandler.java | 8 ++ .../data/impl/handler/QueryHandler.java | 32 +---- .../data/impl/handler/QueryRunner.java | 28 +++++ .../data/impl/meta/RepositoryMethod.java | 12 ++ .../data/impl/meta/RequiresTransaction.java | 31 +++++ .../data/impl/tx/InvocationContextWrapper.java | 74 +++++++++++ .../data/impl/tx/TransactionalQueryRunner.java | 94 ++++++++++++++ .../deltaspike/data/impl/util/ClassUtils.java | 46 +++++++ .../deltaspike/data/impl/util/ProxyUtils.java | 53 ++++++++ .../impl/tx/TransactionalQueryRunnerTest.java | 123 +++++++++++++++++++ .../tx/TransactionalQueryRunnerWrapper.java | 65 ++++++++++ .../service/ExtendedRepositoryInterface.java | 14 +++ .../data/test/util/TestDeployments.java | 24 +++- .../impl/entitymanager/EntityManagerHolder.java | 49 ++++++++ .../ResourceLocalTransactionStrategy.java | 38 ++++-- 18 files changed, 667 insertions(+), 62 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/pom.xml ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/pom.xml b/deltaspike/modules/data/impl/pom.xml index 7b262fb..f54bef6 100755 --- a/deltaspike/modules/data/impl/pom.xml +++ b/deltaspike/modules/data/impl/pom.xml @@ -113,6 +113,12 @@ <artifactId>deltaspike-partial-bean-module-impl</artifactId> <scope>runtime</scope> </dependency> + + <dependency> + <groupId>org.apache.deltaspike.modules</groupId> + <artifactId>deltaspike-jpa-module-impl</artifactId> + <scope>compile</scope> + </dependency> <!-- Tests --> @@ -121,6 +127,12 @@ <artifactId>shrinkwrap-resolver-impl-maven</artifactId> <scope>test</scope> </dependency> + + <dependency> + <groupId>org.jboss.shrinkwrap.descriptors</groupId> + <artifactId>shrinkwrap-descriptors-impl-javaee</artifactId> + <scope>test</scope> + </dependency> </dependencies> http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/DelegateQueryBuilder.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/DelegateQueryBuilder.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/DelegateQueryBuilder.java index 0668ce7..1d9ca5d 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/DelegateQueryBuilder.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/DelegateQueryBuilder.java @@ -18,6 +18,9 @@ */ package org.apache.deltaspike.data.impl.builder; +import static org.apache.deltaspike.data.impl.util.ClassUtils.contains; +import static org.apache.deltaspike.data.impl.util.ClassUtils.extract; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Set; @@ -87,24 +90,6 @@ public class DelegateQueryBuilder extends QueryBuilder return null; } - private boolean contains(Class<?> obj, Method method) - { - return extract(obj, method) != null; - } - - private Method extract(Class<?> obj, Method method) - { - try - { - String name = method.getName(); - return obj != null ? obj.getMethod(name, method.getParameterTypes()) : null; - } - catch (NoSuchMethodException e) - { - return null; - } - } - private Object invoke(DelegateQueryHandler delegate, CdiQueryInvocationContext context) { try http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java index 8900d68..d553114 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java @@ -226,4 +226,9 @@ public class CdiQueryInvocationContext implements QueryInvocationContext return repoMethod.getSingleResultStyle(); } + public Object getProxy() + { + return proxy; + } + } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/EntityRepositoryHandler.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/EntityRepositoryHandler.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/EntityRepositoryHandler.java index 709d733..0c6ce40 100755 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/EntityRepositoryHandler.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/EntityRepositoryHandler.java @@ -37,6 +37,7 @@ import javax.persistence.metamodel.SingularAttribute; import org.apache.deltaspike.data.api.EntityRepository; import org.apache.deltaspike.data.impl.builder.QueryBuilder; +import org.apache.deltaspike.data.impl.meta.RequiresTransaction; import org.apache.deltaspike.data.impl.property.Property; import org.apache.deltaspike.data.impl.property.query.NamedPropertyCriteria; import org.apache.deltaspike.data.impl.property.query.PropertyQueries; @@ -59,6 +60,7 @@ public class EntityRepositoryHandler<E, PK extends Serializable> private QueryInvocationContext context; @Override + @RequiresTransaction public E save(E entity) { if (context.isNew(entity)) @@ -70,6 +72,7 @@ public class EntityRepositoryHandler<E, PK extends Serializable> } @Override + @RequiresTransaction public E saveAndFlush(E entity) { E result = save(entity); @@ -78,6 +81,7 @@ public class EntityRepositoryHandler<E, PK extends Serializable> } @Override + @RequiresTransaction public E saveAndFlushAndRefresh(E entity) { E result = saveAndFlush(entity); @@ -86,6 +90,7 @@ public class EntityRepositoryHandler<E, PK extends Serializable> } @Override + @RequiresTransaction public void refresh(E entity) { entityManager().refresh(entity); @@ -161,12 +166,14 @@ public class EntityRepositoryHandler<E, PK extends Serializable> } @Override + @RequiresTransaction public void remove(E entity) { entityManager().remove(entity); } @Override + @RequiresTransaction public void removeAndFlush(E entity) { entityManager().remove(entity); @@ -174,6 +181,7 @@ public class EntityRepositoryHandler<E, PK extends Serializable> } @Override + @RequiresTransaction public void flush() { entityManager().flush(); http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryHandler.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryHandler.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryHandler.java index 6ce2af8..4a0ecd0 100755 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryHandler.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryHandler.java @@ -18,12 +18,11 @@ */ package org.apache.deltaspike.data.impl.handler; +import static org.apache.deltaspike.data.impl.util.ProxyUtils.extractFromProxy; + import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.Arrays; -import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -64,6 +63,9 @@ public class QueryHandler implements Serializable, InvocationHandler @Inject private EntityManagerLookup entityManagerLookup; + @Inject + private QueryRunner runner; + @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { @@ -75,7 +77,7 @@ public class QueryHandler implements Serializable, InvocationHandler RepositoryMethod repoMethod = components.lookupMethod(repo.getRepositoryClass(), method); queryContext = createContext(proxy, method, args, repo, repoMethod); QueryBuilder builder = queryBuilder.build(repoMethod, queryContext); - Object result = builder.executeQuery(queryContext); + Object result = runner.executeQuery(builder, queryContext); return result; } catch (PersistenceException e) @@ -107,26 +109,4 @@ public class QueryHandler implements Serializable, InvocationHandler return queryContext; } - protected List<Class<?>> extractFromProxy(Class<?> proxyClass) - { - List<Class<?>> result = new LinkedList<Class<?>>(); - result.add(proxyClass); - if (isInterfaceProxy(proxyClass)) - { - result.addAll(Arrays.asList(proxyClass.getInterfaces())); - } - else - { - result.add(proxyClass.getSuperclass()); - } - return result; - } - - private boolean isInterfaceProxy(Class<?> proxyClass) - { - Class<?>[] interfaces = proxyClass.getInterfaces(); - return Proxy.class.equals(proxyClass.getSuperclass()) && - interfaces != null && interfaces.length > 0; - } - } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryRunner.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryRunner.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryRunner.java new file mode 100644 index 0000000..a0f9206 --- /dev/null +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryRunner.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.deltaspike.data.impl.handler; + +import org.apache.deltaspike.data.impl.builder.QueryBuilder; + +public interface QueryRunner +{ + + Object executeQuery(QueryBuilder builder, CdiQueryInvocationContext context) throws Throwable; + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java index ac90562..ff051fe 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java @@ -27,10 +27,12 @@ import java.util.Set; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; +import javax.persistence.LockModeType; import org.apache.deltaspike.core.api.provider.BeanManagerProvider; import org.apache.deltaspike.core.api.provider.BeanProvider; import org.apache.deltaspike.core.api.provider.DependentProvider; +import org.apache.deltaspike.data.api.Modifying; import org.apache.deltaspike.data.api.Query; import org.apache.deltaspike.data.api.SingleResultType; import org.apache.deltaspike.data.api.mapping.MappingConfig; @@ -230,4 +232,14 @@ public class RepositoryMethod return methodPrefix.getSingleResultStyle(); } + public boolean requiresTransaction() + { + boolean hasLockMode = false; + if (method.isAnnotationPresent(Query.class)) + { + hasLockMode = !method.getAnnotation(Query.class).lock().equals(LockModeType.NONE); + } + return hasLockMode || method.isAnnotationPresent(Modifying.class); + } + } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RequiresTransaction.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RequiresTransaction.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RequiresTransaction.java new file mode 100644 index 0000000..c59593e --- /dev/null +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RequiresTransaction.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.deltaspike.data.impl.meta; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target(METHOD) +@Retention(RUNTIME) +public @interface RequiresTransaction +{ +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/tx/InvocationContextWrapper.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/tx/InvocationContextWrapper.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/tx/InvocationContextWrapper.java new file mode 100644 index 0000000..e8692cb --- /dev/null +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/tx/InvocationContextWrapper.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.deltaspike.data.impl.tx; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import javax.interceptor.InvocationContext; + +import org.apache.deltaspike.data.impl.handler.CdiQueryInvocationContext; + +public abstract class InvocationContextWrapper implements InvocationContext +{ + + private final CdiQueryInvocationContext context; + + public InvocationContextWrapper(CdiQueryInvocationContext context) + { + this.context = context; + } + + @Override + public Map<String, Object> getContextData() + { + return new HashMap<String, Object>(0); + } + + @Override + public Method getMethod() + { + return context.getMethod(); + } + + @Override + public Object[] getParameters() + { + return context.getMethodParameters(); + } + + @Override + public Object getTarget() + { + return context.getProxy(); + } + + @Override + public Object getTimer() + { + return null; + } + + @Override + public void setParameters(Object[] args) + { + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/tx/TransactionalQueryRunner.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/tx/TransactionalQueryRunner.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/tx/TransactionalQueryRunner.java new file mode 100644 index 0000000..d1b7d00 --- /dev/null +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/tx/TransactionalQueryRunner.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.deltaspike.data.impl.tx; + +import static org.apache.deltaspike.data.impl.util.ClassUtils.contains; +import static org.apache.deltaspike.data.impl.util.ClassUtils.extract; + +import java.lang.reflect.Method; + +import javax.inject.Inject; + +import org.apache.deltaspike.data.impl.builder.QueryBuilder; +import org.apache.deltaspike.data.impl.handler.CdiQueryInvocationContext; +import org.apache.deltaspike.data.impl.handler.EntityRepositoryHandler; +import org.apache.deltaspike.data.impl.handler.QueryRunner; +import org.apache.deltaspike.data.impl.meta.RequiresTransaction; +import org.apache.deltaspike.jpa.impl.entitymanager.EntityManagerHolder; +import org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy; + +public class TransactionalQueryRunner implements QueryRunner +{ + + @Inject + private TransactionStrategy strategy; + + @Inject + private EntityManagerHolder entityManagerHolder; + + @Override + public Object executeQuery(final QueryBuilder builder, final CdiQueryInvocationContext context) + throws Throwable + { + if (needsTransaction(context)) + { + try + { + entityManagerHolder.set(context.getEntityManager()); + return executeTransactional(builder, context); + } + finally + { + entityManagerHolder.dispose(); + } + } + return executeNonTransactional(builder, context); + } + + protected Object executeNonTransactional(final QueryBuilder builder, final CdiQueryInvocationContext context) + { + return builder.executeQuery(context); + } + + protected Object executeTransactional(final QueryBuilder builder, final CdiQueryInvocationContext context) + throws Exception + { + return strategy.execute(new InvocationContextWrapper(context) + { + @Override + public Object proceed() throws Exception + { + return builder.executeQuery(context); + } + }); + } + + private boolean needsTransaction(CdiQueryInvocationContext context) + { + boolean requiresTx = false; + Method method = context.getMethod(); + if (contains(EntityRepositoryHandler.class, method)) + { + Method executed = extract(EntityRepositoryHandler.class, method); + requiresTx = executed.isAnnotationPresent(RequiresTransaction.class); + } + return requiresTx || context.getRepositoryMethod().requiresTransaction(); + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/util/ClassUtils.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/util/ClassUtils.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/util/ClassUtils.java new file mode 100644 index 0000000..d1e73ff --- /dev/null +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/util/ClassUtils.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.deltaspike.data.impl.util; + +import java.lang.reflect.Method; + +public class ClassUtils +{ + private ClassUtils() + { + } + + public static boolean contains(Class<?> obj, Method method) + { + return extract(obj, method) != null; + } + + public static Method extract(Class<?> clazz, Method method) + { + try + { + String name = method.getName(); + return clazz != null ? clazz.getMethod(name, method.getParameterTypes()) : null; + } + catch (NoSuchMethodException e) + { + return null; + } + } +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/util/ProxyUtils.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/util/ProxyUtils.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/util/ProxyUtils.java new file mode 100644 index 0000000..d683e32 --- /dev/null +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/util/ProxyUtils.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.deltaspike.data.impl.util; + +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class ProxyUtils +{ + private ProxyUtils() + { + } + + public static List<Class<?>> extractFromProxy(Class<?> proxyClass) + { + List<Class<?>> result = new LinkedList<Class<?>>(); + result.add(proxyClass); + if (isInterfaceProxy(proxyClass)) + { + result.addAll(Arrays.asList(proxyClass.getInterfaces())); + } + else + { + result.add(proxyClass.getSuperclass()); + } + return result; + } + + public static boolean isInterfaceProxy(Class<?> proxyClass) + { + Class<?>[] interfaces = proxyClass.getInterfaces(); + return Proxy.class.equals(proxyClass.getSuperclass()) && + interfaces != null && interfaces.length > 0; + } +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/tx/TransactionalQueryRunnerTest.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/tx/TransactionalQueryRunnerTest.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/tx/TransactionalQueryRunnerTest.java new file mode 100644 index 0000000..c6c7346 --- /dev/null +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/tx/TransactionalQueryRunnerTest.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.deltaspike.data.impl.tx; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.apache.deltaspike.data.test.domain.Simple; +import org.apache.deltaspike.data.test.service.ExtendedRepositoryInterface; +import org.apache.deltaspike.data.test.util.TestDeployments; +import org.apache.deltaspike.test.category.WebProfileCategory; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.junit.InSequence; +import org.jboss.shrinkwrap.api.Archive; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +@Category(WebProfileCategory.class) +@RunWith(Arquillian.class) +public class TransactionalQueryRunnerTest +{ + + private static final String NAME = "should_run_in_transaction"; + + @Deployment + public static Archive<?> deployment() + { + return TestDeployments.initDeployment() + .addClasses(ExtendedRepositoryInterface.class) + .addPackage(Simple.class.getPackage()); + } + + @Inject + private ExtendedRepositoryInterface repository; + + @Produces + @PersistenceContext + private EntityManager entityManager; + + @Inject + private TransactionalQueryRunnerWrapper wrapper; + + @Test + @InSequence(1) + public void should_run_modifying_in_transaction() throws Exception + { + // when + repository.deleteAll(); + + // then + assertTrue(wrapper.isRunInTx()); + } + + @Test + @InSequence(2) + public void should_save_in_transaction() throws Exception + { + // given + Simple simple = new Simple(NAME); + + // when + simple = repository.save(simple); + + // then + assertNotNull(simple.getId()); + assertTrue(wrapper.isRunInTx()); + } + + @Test + @InSequence(3) + public void should_find_with_lockmode_in_transaction() throws Exception + { + // when + Simple simple = repository.findByName(NAME); + + // then + assertNotNull(simple); + assertTrue(wrapper.isRunInTx()); + } + + @Test + @InSequence(4) + public void should_find_no_lock_without_transaction() throws Exception + { + // when + Simple simple = repository.findByNameNoLock(NAME); + + // then + assertNotNull(simple); + assertTrue(wrapper.isRunInNonTx()); + } + + @Before + public void init() + { + wrapper.reset(); + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/tx/TransactionalQueryRunnerWrapper.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/tx/TransactionalQueryRunnerWrapper.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/tx/TransactionalQueryRunnerWrapper.java new file mode 100644 index 0000000..14e0e63 --- /dev/null +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/tx/TransactionalQueryRunnerWrapper.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.deltaspike.data.impl.tx; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Specializes; + +import org.apache.deltaspike.data.impl.builder.QueryBuilder; +import org.apache.deltaspike.data.impl.handler.CdiQueryInvocationContext; + +@Specializes +@ApplicationScoped +public class TransactionalQueryRunnerWrapper extends TransactionalQueryRunner +{ + + private boolean runInTx = false; + private boolean runInNonTx = false; + + public void reset() + { + runInTx = false; + runInNonTx = false; + } + + @Override + protected Object executeNonTransactional(QueryBuilder builder, CdiQueryInvocationContext context) + { + runInNonTx = true; + return super.executeNonTransactional(builder, context); + } + + @Override + protected Object executeTransactional(QueryBuilder builder, CdiQueryInvocationContext context) throws Exception + { + runInTx = true; + return super.executeTransactional(builder, context); + } + + public boolean isRunInTx() + { + return runInTx; + } + + public boolean isRunInNonTx() + { + return runInNonTx; + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/ExtendedRepositoryInterface.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/ExtendedRepositoryInterface.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/ExtendedRepositoryInterface.java index 49ab7f4..1445d33 100755 --- a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/ExtendedRepositoryInterface.java +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/ExtendedRepositoryInterface.java @@ -18,11 +18,25 @@ */ package org.apache.deltaspike.data.test.service; +import static javax.persistence.LockModeType.PESSIMISTIC_READ; + import org.apache.deltaspike.data.api.EntityRepository; +import org.apache.deltaspike.data.api.Modifying; +import org.apache.deltaspike.data.api.Query; import org.apache.deltaspike.data.api.Repository; import org.apache.deltaspike.data.test.domain.Simple; @Repository public interface ExtendedRepositoryInterface extends EntityRepository<Simple, Long> { + + @Query(lock = PESSIMISTIC_READ) + Simple findByName(String name); + + @Query(named = Simple.BY_NAME_LIKE) + Simple findByNameNoLock(String name); + + @Modifying @Query("delete from Simple") + int deleteAll(); + } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/util/TestDeployments.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/util/TestDeployments.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/util/TestDeployments.java index d002913..0472a40 100755 --- a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/util/TestDeployments.java +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/util/TestDeployments.java @@ -47,13 +47,16 @@ import org.apache.deltaspike.data.impl.builder.QueryBuilder; import org.apache.deltaspike.data.impl.criteria.QueryCriteria; import org.apache.deltaspike.data.impl.handler.QueryHandler; import org.apache.deltaspike.data.impl.meta.RepositoryComponents; +import org.apache.deltaspike.data.impl.meta.RequiresTransaction; import org.apache.deltaspike.data.impl.param.Parameters; import org.apache.deltaspike.data.impl.property.Property; +import org.apache.deltaspike.data.impl.tx.TransactionalQueryRunner; import org.apache.deltaspike.data.impl.util.EntityUtils; import org.apache.deltaspike.data.spi.DelegateQueryHandler; import org.apache.deltaspike.data.spi.QueryInvocationContext; import org.apache.deltaspike.data.test.TransactionalTestCase; import org.apache.deltaspike.data.test.domain.AuditedEntity; +import org.apache.deltaspike.jpa.impl.transaction.EnvironmentAwareTransactionStrategy; import org.apache.deltaspike.test.category.WebProfileCategory; import org.apache.deltaspike.test.utils.CdiContainerUnderTest; import org.jboss.shrinkwrap.api.Archive; @@ -61,9 +64,11 @@ import org.jboss.shrinkwrap.api.ArchivePath; import org.jboss.shrinkwrap.api.ArchivePaths; import org.jboss.shrinkwrap.api.Filter; import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.jboss.shrinkwrap.descriptor.api.Descriptors; +import org.jboss.shrinkwrap.descriptor.api.beans10.BeansDescriptor; import org.jboss.shrinkwrap.impl.base.filter.ExcludeRegExpPaths; import org.jboss.shrinkwrap.resolver.api.maven.Maven; @@ -85,6 +90,12 @@ public abstract class TestDeployments public static WebArchive initDeployment(String testFilter) { Logging.reconfigure(); + String descriptor = Descriptors.create(BeansDescriptor.class) + .addDefaultNamespaces() + .createAlternatives() + .clazz(EnvironmentAwareTransactionStrategy.class.getName()) + .up() + .exportAsString(); WebArchive archive = ShrinkWrap .create(WebArchive.class, "test.war") .addAsLibrary(createApiArchive()) @@ -98,7 +109,7 @@ public abstract class TestDeployments ArchivePaths.create("classes/META-INF/persistence.xml")) .addAsWebInfResource("META-INF/services/javax.enterprise.inject.spi.Extension", ArchivePaths.create("classes/META-INF/services/javax.enterprise.inject.spi.Extension")) - .addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml")); + .addAsWebInfResource(new StringAsset(descriptor), ArchivePaths.create("beans.xml")); return addDependencies(archive); } @@ -113,7 +124,8 @@ public abstract class TestDeployments RepositoryComponents.class.getPackage(), Parameters.class.getPackage(), EntityUtils.class.getPackage(), - Property.class.getPackage() + Property.class.getPackage(), + TransactionalQueryRunner.class.getPackage() }; } @@ -128,7 +140,7 @@ public abstract class TestDeployments .addClasses(Criteria.class, QuerySelection.class, CriteriaSupport.class) .addClasses(CreatedOn.class, CurrentUser.class, ModifiedBy.class, ModifiedOn.class) .addClasses(MappingConfig.class, QueryInOutMapper.class) - .addClasses(DelegateQueryHandler.class, QueryInvocationContext.class); + .addClasses(DelegateQueryHandler.class, QueryInvocationContext.class, RequiresTransaction.class); } public static WebArchive addDependencies(WebArchive archive) @@ -138,7 +150,9 @@ public abstract class TestDeployments "org.apache.deltaspike.core:deltaspike-core-api", "org.apache.deltaspike.core:deltaspike-core-impl", "org.apache.deltaspike.modules:deltaspike-partial-bean-module-api", - "org.apache.deltaspike.modules:deltaspike-partial-bean-module-impl") + "org.apache.deltaspike.modules:deltaspike-partial-bean-module-impl", + "org.apache.deltaspike.modules:deltaspike-jpa-module-api", + "org.apache.deltaspike.modules:deltaspike-jpa-module-impl") .withTransitivity() .asFile()); if (CdiContainerUnderTest.is("owb-.*") || http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/entitymanager/EntityManagerHolder.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/entitymanager/EntityManagerHolder.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/entitymanager/EntityManagerHolder.java new file mode 100644 index 0000000..2d3a5a4 --- /dev/null +++ b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/entitymanager/EntityManagerHolder.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.deltaspike.jpa.impl.entitymanager; + +import javax.enterprise.context.ApplicationScoped; +import javax.persistence.EntityManager; + +@ApplicationScoped +public class EntityManagerHolder +{ + + private final ThreadLocal<EntityManager> entityManager = new ThreadLocal<EntityManager>(); + + public void set(EntityManager entityManager) + { + this.entityManager.set(entityManager); + } + + public boolean isSet() + { + return get() != null; + } + + public EntityManager get() + { + return entityManager.get(); + } + + public void dispose() + { + entityManager.remove(); + } +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/db37a83a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/ResourceLocalTransactionStrategy.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/ResourceLocalTransactionStrategy.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/ResourceLocalTransactionStrategy.java index 717206c..d5819ae 100644 --- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/ResourceLocalTransactionStrategy.java +++ b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/ResourceLocalTransactionStrategy.java @@ -19,14 +19,15 @@ package org.apache.deltaspike.jpa.impl.transaction; -import org.apache.deltaspike.core.api.literal.AnyLiteral; -import org.apache.deltaspike.core.util.ProxyUtils; -import org.apache.deltaspike.jpa.api.transaction.Transactional; -import org.apache.deltaspike.jpa.impl.transaction.context.EntityManagerEntry; -import org.apache.deltaspike.jpa.impl.transaction.context.TransactionBeanStorage; -import org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy; +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Default; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.inject.Inject; @@ -34,11 +35,13 @@ import javax.interceptor.InvocationContext; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; -import java.lang.annotation.Annotation; -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; +import org.apache.deltaspike.core.api.literal.AnyLiteral; +import org.apache.deltaspike.core.util.ProxyUtils; +import org.apache.deltaspike.jpa.api.transaction.Transactional; +import org.apache.deltaspike.jpa.impl.entitymanager.EntityManagerHolder; +import org.apache.deltaspike.jpa.impl.transaction.context.EntityManagerEntry; +import org.apache.deltaspike.jpa.impl.transaction.context.TransactionBeanStorage; +import org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy; /** * <p>Default implementation of our plugable TransactionStrategy. @@ -70,6 +73,10 @@ public class ResourceLocalTransactionStrategy implements TransactionStrategy @Inject private TransactionStrategyHelper transactionHelper; + @Inject + private EntityManagerHolder emHolder; + + @Override public Object execute(InvocationContext invocationContext) throws Exception { Transactional transactionalAnnotation = transactionHelper.extractTransactionalAnnotation(invocationContext); @@ -77,8 +84,9 @@ public class ResourceLocalTransactionStrategy implements TransactionStrategy Class targetClass = ProxyUtils.getUnproxiedClass(invocationContext.getTarget().getClass()); //see DELTASPIKE-517 // all the configured qualifier keys - Set<Class<? extends Annotation>> emQualifiers = transactionHelper.resolveEntityManagerQualifiers( - transactionalAnnotation, targetClass); + Set<Class<? extends Annotation>> emQualifiers = emHolder.isSet() ? + new HashSet<Class<? extends Annotation>>(Arrays.asList(Default.class)) : + transactionHelper.resolveEntityManagerQualifiers(transactionalAnnotation, targetClass); TransactionBeanStorage transactionBeanStorage = TransactionBeanStorage.getInstance(); @@ -307,6 +315,10 @@ public class ResourceLocalTransactionStrategy implements TransactionStrategy private EntityManager resolveEntityManagerForQualifier(Class<? extends Annotation> emQualifier) { + if (emHolder.isSet()) + { + return emHolder.get(); + } Bean<EntityManager> entityManagerBean = resolveEntityManagerBean(emQualifier); if (entityManagerBean == null)
