/* * 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.core.impl.exception.control; import org.apache.deltaspike.core.api.exception.control.HandlerMethod; import org.apache.deltaspike.core.api.exception.control.BeforeHandles; import org.apache.deltaspike.core.api.exception.control.Handles; import org.apache.deltaspike.core.api.exception.control.event.ExceptionEvent; import org.apache.deltaspike.core.api.literal.AnyLiteral; import org.apache.deltaspike.core.api.provider.BeanProvider; import org.apache.deltaspike.core.util.BeanUtils; import org.apache.deltaspike.core.util.metadata.builder.ImmutableInjectionPoint; import org.apache.deltaspike.core.util.metadata.builder.InjectableMethod; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.Typed; import javax.enterprise.inject.spi.AnnotatedMethod; import javax.enterprise.inject.spi.AnnotatedParameter; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.InjectionPoint; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * Implementation of {@link HandlerMethod}. * * @param <T> Type of the exception this handler handles. */ @Typed() public class HandlerMethodImpl<T extends Throwable> implements HandlerMethod<T> { private final Class declaringBeanClass; private final Bean<?> declaringBean; private final Set<Annotation> qualifiers; private final Type exceptionType; private final AnnotatedMethod<?> handler; private final boolean before; private final int ordinal; private final Method javaMethod; private final AnnotatedParameter<?> handlerParameter; private Set<InjectionPoint> injectionPoints; /** * Sole Constructor. * * @param method found handler * @param bm active BeanManager * @throws IllegalArgumentException if method is null, has no params or first param is not annotated with * {@link Handles} or {@link BeforeHandles} */ public HandlerMethodImpl(final Bean<?> handlerDeclaringBean, final AnnotatedMethod<?> method, final BeanManager bm) { //validation is done by the extension final Set<Annotation> tmpQualifiers = new HashSet<Annotation>(); declaringBean = handlerDeclaringBean; handler = method; javaMethod = method.getJavaMember(); handlerParameter = findHandlerParameter(method); if (!handlerParameter.isAnnotationPresent(Handles.class) && !handlerParameter.isAnnotationPresent(BeforeHandles.class)) { throw new IllegalArgumentException("Method is not annotated with @Handles or @BeforeHandles"); } before = handlerParameter.getAnnotation(BeforeHandles.class) != null; if (before) { ordinal = handlerParameter.getAnnotation(BeforeHandles.class).ordinal(); } else { ordinal = handlerParameter.getAnnotation(Handles.class).ordinal(); } tmpQualifiers.addAll(BeanUtils.getQualifiers(bm, handlerParameter.getAnnotations())); if (tmpQualifiers.isEmpty()) { tmpQualifiers.add(new AnyLiteral()); } qualifiers = tmpQualifiers; declaringBeanClass = method.getJavaMember().getDeclaringClass(); exceptionType = ((ParameterizedType) handlerParameter.getBaseType()).getActualTypeArguments()[0]; } /** * Determines if the given method is a handler by looking for the {@link Handles} annotation on a parameter. * * @param method method to search * @return true if {@link Handles} is found, false otherwise */ public static boolean isHandler(final AnnotatedMethod<?> method) { if (method == null) { throw new IllegalArgumentException("Method must not be null"); } for (AnnotatedParameter<?> param : method.getParameters()) { if (param.isAnnotationPresent(Handles.class) || param.isAnnotationPresent(BeforeHandles.class)) { return true; } } return false; } public static AnnotatedParameter<?> findHandlerParameter(final AnnotatedMethod<?> method) { if (!isHandler(method)) { throw new IllegalArgumentException("Method is not a valid handler"); } AnnotatedParameter<?> returnParam = null; for (AnnotatedParameter<?> param : method.getParameters()) { if (param.isAnnotationPresent(Handles.class) || param.isAnnotationPresent(BeforeHandles.class)) { returnParam = param; break; } } return returnParam; } public Bean<?> getDeclaringBean() { return declaringBean; } /** * {@inheritDoc} */ @Override public Set<Annotation> getQualifiers() { return Collections.unmodifiableSet(qualifiers); } /** * {@inheritDoc} */ @Override public Type getExceptionType() { return exceptionType; } /** * {@inheritDoc} */ @Override public void notify(final ExceptionEvent<T> event, BeanManager beanManager) throws Exception { CreationalContext<?> ctx = null; try { ctx = beanManager.createCreationalContext(null); @SuppressWarnings("unchecked") Object handlerInstance = BeanProvider.getContextualReference(declaringBeanClass); InjectableMethod<?> im = createInjectableMethod(handler, getDeclaringBean(), beanManager); im.invoke(handlerInstance, ctx, new OutboundParameterValueRedefiner(event, this)); } finally { if (ctx != null) { ctx.release(); } } } private <X> InjectableMethod<X> createInjectableMethod(AnnotatedMethod<X> handlerMethod, Bean<?> bean, BeanManager bm) { return new InjectableMethod<X>(handlerMethod, bean, bm); } /** * {@inheritDoc} */ @Override public boolean isBeforeHandler() { return before; } /** * {@inheritDoc} */ @Override public int getOrdinal() { return ordinal; } public AnnotatedParameter<?> getHandlerParameter() { return handlerParameter; } public Method getJavaMethod() { return handler.getJavaMember(); } /** * Obtain all the injection points for the handler * * @param bm a BeanManager to use to obtain the beans */ public Set<InjectionPoint> getInjectionPoints(final BeanManager bm) { if (injectionPoints == null) { injectionPoints = new HashSet<InjectionPoint>(handler.getParameters().size() - 1); for (AnnotatedParameter<?> param : handler.getParameters()) { if (!param.equals(handlerParameter)) { injectionPoints.add( new ImmutableInjectionPoint(param, bm, getDeclaringBean(), false, false)); } } } return new HashSet<InjectionPoint>(injectionPoints); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || !HandlerMethod.class.isAssignableFrom(o.getClass())) { return false; } HandlerMethod<?> that = (HandlerMethod<?>) o; if (!qualifiers.equals(that.getQualifiers())) { return false; } if (isBeforeHandler() != that.isBeforeHandler()) { return false; } //noinspection SimplifiableIfStatement if (!exceptionType.equals(that.getExceptionType())) { return false; } return ordinal == that.getOrdinal(); } @Override public int hashCode() { int result = declaringBeanClass.hashCode(); result = 5 * result + qualifiers.hashCode(); result = 5 * result + exceptionType.hashCode(); result = 5 * result + ordinal; result = 5 * result + javaMethod.hashCode(); result = 5 * result + handlerParameter.hashCode(); return result; } @Override public String toString() { return "{Qualifiers: " + qualifiers + ", " + "Handles Type: " + exceptionType + ", " + "Before: " + before + ", " + "Precedence: " + ordinal + ", Method: " + handler.getJavaMember().getName() + "}"; } }