001/*
002  GRANITE DATA SERVICES
003  Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005  This file is part of Granite Data Services.
006
007  Granite Data Services is free software; you can redistribute it and/or modify
008  it under the terms of the GNU Library General Public License as published by
009  the Free Software Foundation; either version 2 of the License, or (at your
010  option) any later version.
011
012  Granite Data Services is distributed in the hope that it will be useful, but
013  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015  for more details.
016
017  You should have received a copy of the GNU Library General Public License
018  along with this library; if not, see <http://www.gnu.org/licenses/>.
019*/
020
021package org.granite.tide.seam.async;
022
023import java.lang.annotation.Annotation;
024import java.util.Date;
025
026import org.granite.tide.seam.AbstractSeamServiceContext;
027import org.granite.tide.seam.TideInvocation;
028import org.jboss.seam.Component;
029import org.jboss.seam.annotations.async.Asynchronous;
030import org.jboss.seam.annotations.async.Duration;
031import org.jboss.seam.annotations.async.Expiration;
032import org.jboss.seam.annotations.async.FinalExpiration;
033import org.jboss.seam.annotations.async.IntervalCron;
034import org.jboss.seam.annotations.async.IntervalDuration;
035import org.jboss.seam.annotations.intercept.AroundInvoke;
036import org.jboss.seam.annotations.intercept.Interceptor;
037import org.jboss.seam.annotations.intercept.InterceptorType;
038import org.jboss.seam.async.AbstractDispatcher;
039import org.jboss.seam.async.AsynchronousInterceptor;
040import org.jboss.seam.contexts.Contexts;
041import org.jboss.seam.intercept.AbstractInterceptor;
042import org.jboss.seam.intercept.InvocationContext;
043
044/**
045 * Slightly modified version of the Seam asynchronous interceptor which saves the current username
046 * 
047 * Dispatches method calls to @Asynchronous methods
048 * asynchronously, and returns the "timer" object
049 * if necessary.
050 * 
051 * @author William DRAI
052 *
053 */
054@Interceptor(stateless=true, type=InterceptorType.CLIENT, around={ AsynchronousInterceptor.class })
055public class TideAsynchronousInterceptor extends AbstractInterceptor {
056    
057    private static final long serialVersionUID = 9194177339867853303L;
058    
059    
060    @AroundInvoke
061    public Object aroundInvoke(InvocationContext invocation) throws Exception {
062        if (invocation.getTarget() instanceof AsynchronousInvoker) {
063                if (Contexts.getEventContext().isSet(AbstractDispatcher.EXECUTING_ASYNCHRONOUS_CALL))
064                        TideInvocation.init();
065            return invocation.proceed();
066        }
067        
068        boolean scheduleAsync = invocation.getMethod().isAnnotationPresent(Asynchronous.class) && 
069            (!Contexts.getEventContext().isSet(AbstractDispatcher.EXECUTING_ASYNCHRONOUS_CALL) 
070                    || Contexts.getEventContext().isSet("org.jboss.seam.async.AsynchronousIntercepter.REENTRANT"));
071        if (scheduleAsync) {
072            AbstractSeamServiceContext serviceContext = (AbstractSeamServiceContext)Component.getInstance(AbstractSeamServiceContext.COMPONENT_NAME, false);
073            if (serviceContext != null && serviceContext.getSessionId() != null) {
074                Annotation[][] parameterAnnotations = invocation.getMethod().getParameterAnnotations();
075                Long duration = null;
076                Date expiration = null;
077                Date finalExpiration = null;
078                Long intervalDuration = null;
079                String intervalCron = null;
080                for (int i = 0; i < parameterAnnotations.length; i++) {
081                    Annotation[] annotations = parameterAnnotations[i];
082                    for (Annotation annotation : annotations) {
083                        if (annotation.annotationType().equals(Duration.class))
084                            duration = (Long)invocation.getParameters()[i];
085                        else if (annotation.annotationType().equals(Expiration.class))
086                            expiration = (Date)invocation.getParameters()[i];
087                        else if (annotation.annotationType().equals(FinalExpiration.class))
088                            finalExpiration = (Date)invocation.getParameters()[i];
089                        else if (annotation.annotationType().equals(IntervalDuration.class))
090                            intervalDuration = (Long)invocation.getParameters()[i];
091                        else if (annotation.annotationType().equals(IntervalCron.class))
092                            intervalCron = (String)invocation.getParameters()[i];
093                    }
094                }
095                
096                String targetComponentName = getComponent().getName();
097                Class<?> targetComponentClass = getComponent().getBeanClass();
098                String methodName = invocation.getMethod().getName();
099                Class<?>[] paramTypes = invocation.getMethod().getParameterTypes();
100                Object[] params = invocation.getParameters();
101                
102                AsynchronousInvoker invoker = (AsynchronousInvoker)Component.getInstance("org.granite.tide.seam.asynchronousInvoker");
103                
104                AsyncContext asyncContext = serviceContext.getAsyncContext();
105                
106                if (intervalCron != null)
107                    return invoker.invokeAsynchronousCron(asyncContext, targetComponentName, targetComponentClass, methodName, paramTypes, params, 
108                            duration, expiration, finalExpiration, intervalCron);
109                
110                if (finalExpiration != null)
111                    return invoker.invokeAsynchronousDuration(asyncContext, targetComponentName, targetComponentClass, methodName, paramTypes, params, 
112                            duration, expiration, finalExpiration, intervalDuration);
113                
114                return invoker.invokeAsynchronousDuration(asyncContext, targetComponentName, targetComponentClass, methodName, paramTypes, params, 
115                        duration, expiration, intervalDuration);
116            }
117        }
118        
119        return invocation.proceed();
120    }
121
122        // Needed for Seam 2.1
123    public boolean isInterceptorEnabled() {
124        return true;
125    }
126}