/**
* 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;
}
}