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