/** * JBoss, Home of Professional Open Source * Copyright 2011, Red Hat, Inc. and/or its affiliates, and individual * contributors by the @authors tag. See the copyright.txt in the * distribution for a full listing of individual contributors. * * 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 * 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.jboss.seam.cron.spi.asynchronous; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.concurrent.Future; import javax.enterprise.inject.spi.BeanManager; import javax.inject.Inject; import javax.interceptor.InvocationContext; import org.jboss.seam.cron.api.asynchronous.AsyncResult; import org.jboss.seam.cron.api.asynchronous.Asynchronous; import org.jboss.seam.cron.impl.scheduling.exception.InternalException; import org.jboss.solder.logging.Logger; import static org.jboss.seam.cron.spi.asynchronous.AsynchronousInterceptor.INVOKED_IN_THREAD; /** * This class handles the invocation of the #{@link Asynchronous} method, unwrapping of the * results out of a "dummy" #{@link AsyncResult} if necessary, and firing post-execution * events with the results if any. It is designed as a managed bean to be instantiated via * #{@literal @Inject Instance<Invoker>}. * * @author Peter Royle */ public class Invoker { @Inject BeanManager beanMan; private InvocationContext ic; private boolean popResultsFromFuture = false; private final Logger log = Logger.getLogger(Invoker.class); public Invoker() { } /** * @param ic The #{@link InvocationContext} which will be executed. */ public void setInvocationContext(final InvocationContext ic) { this.ic = ic; } /** * Set to true if the #{@link InvocationContext} returns a "dummy" #{@link Future}. * In that case we need to explicitly pop the return value out of it as it will have * already been wrapped in an #{@link AsyncResult} by the #{@link AsynchronousInterceptor}. * @param popResultsFromFuture */ protected void setPopResultsFromFuture(final boolean popResultsFromFuture) { this.popResultsFromFuture = popResultsFromFuture; } /** * Execute the #{@link InvocationContext}, unwrap the results from their #{@link AsyncResult} * if necessary and fire a post-execution event. * * @return The result of the method invocation, unwrapped if #{@literal popResultsFromFuture} is true * (ie: the return type of the method is a #{@link Future}). * @throws Exception Includes any exception thrown by the invoked method. */ public Object executeInvocationContext() throws Exception { // This will be the basic form, with the result available immediately Object result; // housekeeping if (ic == null || ic.getMethod() == null) { throw new InternalException("Failed to provide an InvocationContext/method to this " + this.getClass().getName()); } final Method method = ic.getMethod(); if (log.isTraceEnabled()) { log.trace("Running Invocation Context for " + method.getName()); } // grab qualifiers from the method to use for the post-execution event final ArrayList<Annotation> qualifiers = new ArrayList<Annotation>(); for (Annotation ant : method.getAnnotations()) { if (beanMan.isQualifier(ant.annotationType())) { qualifiers.add(ant); } } ic.getContextData().put(INVOKED_IN_THREAD, Boolean.TRUE); result = ic.proceed(); if (popResultsFromFuture) { // pop the value out of the "dummy" AsynchResult as it will be wrapped // in proper AsynchResult by the AsynchronousInterceptor result = ((Future) result).get(); } // fire the post execution event if a result was returned. if (result != null) { if (log.isTraceEnabled()) { log.trace("Firing post execution event result: " + result); } beanMan.fireEvent(result, qualifiers.toArray(new Annotation[qualifiers.size()])); } else { if (log.isTraceEnabled()) { if (method.getReturnType().equals(Void.TYPE)) { log.trace("Method invocation on " + method.getName() + ":" + method.getClass().getName() + " returns void, so not firing a post-execution event"); } else { log.trace("Method invocation on " + method.getName() + ":" + method.getClass().getName() + " returned null, so not firing an event"); } } } return result; } }