/* * 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 org.apache.deltaspike.core.util.StringUtils.isNotEmpty; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; 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.core.util.OptionalUtil; 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; import org.apache.deltaspike.data.api.mapping.QueryInOutMapper; import org.apache.deltaspike.data.impl.builder.MethodExpressionException; import org.apache.deltaspike.data.impl.builder.part.QueryRoot; import org.apache.deltaspike.data.impl.builder.result.QueryProcessor; import org.apache.deltaspike.data.impl.builder.result.QueryProcessorFactory; import org.apache.deltaspike.data.impl.handler.CdiQueryInvocationContext; import org.apache.deltaspike.data.impl.util.bean.DependentProviderDestroyable; /** * Stores information about a specific method of a Repository: * <ul> * <li>The reference to the Method reflection object</li> * <li>Whether this method delegates, is annotated or is parsed</li> * <li>A reference to the parent Repository</li> * <li>For parsed Repository methods, also the JPQL string is cached</li> * </ul> */ public class RepositoryMethod { private final Method method; private final MethodType methodType; private final MethodPrefix methodPrefix; private final RepositoryComponent repo; private final QueryRoot queryRoot; private final QueryProcessor queryProcessor; private final Class<? extends QueryInOutMapper<?>> mapper; private final boolean isOptional; private volatile Boolean queryInOutMapperIsNormalScope; public RepositoryMethod(Method method, RepositoryComponent repo) { this.method = method; this.repo = repo; this.methodPrefix = new MethodPrefix(repo.getCustomMethodPrefix(), method.getName()); this.methodType = extractMethodType(); this.queryRoot = initQueryRoot(); this.queryProcessor = QueryProcessorFactory.newInstance(method, methodPrefix).build(); this.mapper = extractMapper(method, repo); this.isOptional = OptionalUtil.isOptionalReturned(this.method); } public boolean returns(Class<?> returnType) { return returnType.equals(method.getReturnType()); } public QueryInOutMapper<?> getQueryInOutMapperInstance(CdiQueryInvocationContext context) { if (!hasQueryInOutMapper()) { return null; } QueryInOutMapper<?> result = null; lazyInit(); if (!queryInOutMapperIsNormalScope) { final DependentProvider<? extends QueryInOutMapper<?>> mappedProvider = BeanProvider.getDependent(mapper); result = mappedProvider.get(); context.addDestroyable(new DependentProviderDestroyable(mappedProvider)); } else { result = BeanProvider.getContextualReference(mapper); } return result; } private MethodType extractMethodType() { if (isAnnotated()) { return MethodType.ANNOTATED; } if (isMethodExpression()) { return MethodType.PARSE; } return MethodType.DELEGATE; } private QueryRoot initQueryRoot() { if (methodType == MethodType.PARSE) { return QueryRoot.create(method.getName(), repo, methodPrefix); } return QueryRoot.UNKNOWN_ROOT; } private boolean isAnnotated() { if (method.isAnnotationPresent(Query.class)) { Query query = method.getAnnotation(Query.class); return isValid(query); } return false; } private boolean isValid(Query query) { return isNotEmpty(query.value()) || isNotEmpty(query.named()); } private boolean isMethodExpression() { if (!Modifier.isAbstract(method.getModifiers())) { return false; } try { QueryRoot.create(method.getName(), repo, methodPrefix); return true; } catch (MethodExpressionException e) { return false; } } private Class<? extends QueryInOutMapper<?>> extractMapper(Method queryMethod, RepositoryComponent repoComponent) { if (queryMethod.isAnnotationPresent(MappingConfig.class)) { return queryMethod.getAnnotation(MappingConfig.class).value(); } if (repoComponent.getRepositoryClass().isAnnotationPresent(MappingConfig.class)) { return repoComponent.getRepositoryClass().getAnnotation(MappingConfig.class).value(); } return null; } //don't trigger this lookup during ProcessAnnotatedType private void lazyInit() { if (queryInOutMapperIsNormalScope == null) { init(BeanManagerProvider.getInstance().getBeanManager()); } } private synchronized void init(BeanManager beanManager) { if (queryInOutMapperIsNormalScope != null) { return; } if (beanManager != null) { final Set<Bean<?>> beans = beanManager.getBeans(mapper); final Class<? extends Annotation> scope = beanManager.resolve(beans).getScope(); queryInOutMapperIsNormalScope = beanManager.isNormalScope(scope); } else { queryInOutMapperIsNormalScope = false; } } public MethodType getMethodType() { return methodType; } public RepositoryComponent getRepository() { return repo; } public QueryRoot getQueryRoot() { return queryRoot; } public QueryProcessor getQueryProcessor() { return queryProcessor; } public boolean hasQueryInOutMapper() { return mapper != null; } public int getDefinedMaxResults() { return this.methodPrefix.getDefinedMaxResults(); } public SingleResultType getSingleResultStyle() { if (method.isAnnotationPresent(Query.class)) { return method.getAnnotation(Query.class).singleResult(); } 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); } public boolean isOptional() { return this.isOptional; } }