001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.cluster;
016    
017    import com.liferay.portal.kernel.bean.IdentifiableBean;
018    import com.liferay.portal.kernel.bean.PortalBeanLocatorUtil;
019    import com.liferay.portal.kernel.bean.PortletBeanLocatorUtil;
020    import com.liferay.portal.kernel.cluster.ClusterExecutorUtil;
021    import com.liferay.portal.kernel.cluster.ClusterInvokeAcceptor;
022    import com.liferay.portal.kernel.cluster.ClusterRequest;
023    import com.liferay.portal.kernel.cluster.Clusterable;
024    import com.liferay.portal.kernel.log.Log;
025    import com.liferay.portal.kernel.log.LogFactoryUtil;
026    import com.liferay.portal.kernel.util.ClassLoaderPool;
027    import com.liferay.portal.kernel.util.MethodHandler;
028    import com.liferay.portal.kernel.util.MethodKey;
029    import com.liferay.portal.kernel.util.Validator;
030    import com.liferay.portal.spring.aop.AnnotationChainableMethodAdvice;
031    import com.liferay.portal.util.ClassLoaderUtil;
032    
033    import java.io.Serializable;
034    
035    import java.lang.annotation.Annotation;
036    import java.lang.reflect.Constructor;
037    
038    import java.util.Map;
039    
040    import org.aopalliance.intercept.MethodInvocation;
041    
042    /**
043     * @author Shuyang Zhou
044     */
045    public class ClusterableAdvice
046            extends AnnotationChainableMethodAdvice<Clusterable> {
047    
048            @Override
049            public void afterReturning(MethodInvocation methodInvocation, Object result)
050                    throws Throwable {
051    
052                    if (!ClusterInvokeThreadLocal.isEnabled()) {
053                            return;
054                    }
055    
056                    Clusterable clusterable = findAnnotation(methodInvocation);
057    
058                    if (clusterable == _nullClusterable) {
059                            return;
060                    }
061    
062                    Object thisObject = methodInvocation.getThis();
063    
064                    if (!(thisObject instanceof IdentifiableBean)) {
065                            _log.error(
066                                    "Not clustering calls for " + thisObject.getClass().getName() +
067                                            " because it does not implement " +
068                                                    IdentifiableBean.class.getName());
069    
070                            return;
071                    }
072    
073                    MethodHandler methodHandler = createMethodHandler(
074                            clusterable.acceptor(), methodInvocation);
075    
076                    ClusterRequest clusterRequest = ClusterRequest.createMulticastRequest(
077                            methodHandler, true);
078    
079                    ClusterExecutorUtil.execute(clusterRequest);
080            }
081    
082            @Override
083            public Clusterable getNullAnnotation() {
084                    return _nullClusterable;
085            }
086    
087            protected MethodHandler createMethodHandler(
088                    Class<? extends ClusterInvokeAcceptor> clusterInvokeAcceptorClass,
089                    MethodInvocation methodInvocation) {
090    
091                    if (clusterInvokeAcceptorClass == ClusterInvokeAcceptor.class) {
092                            clusterInvokeAcceptorClass = null;
093                    }
094    
095                    MethodHandler methodHandler = new MethodHandler(
096                            methodInvocation.getMethod(), methodInvocation.getArguments());
097    
098                    Object thisObject = methodInvocation.getThis();
099    
100                    IdentifiableBean identifiableBean = (IdentifiableBean)thisObject;
101    
102                    String beanIdentifier = identifiableBean.getBeanIdentifier();
103    
104                    Thread currentThread = Thread.currentThread();
105    
106                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
107    
108                    String servletContextName = ClassLoaderPool.getContextName(
109                            contextClassLoader);
110    
111                    Map<String, Serializable> context =
112                            ClusterableContextThreadLocal.collectThreadLocalContext();
113    
114                    return new MethodHandler(
115                            _invokeMethodKey, methodHandler, beanIdentifier, servletContextName,
116                            clusterInvokeAcceptorClass, context);
117            }
118    
119            @SuppressWarnings("unused")
120            private static Object _invoke(
121                            MethodHandler methodHandler, String servletContextName,
122                            String beanIdentifier,
123                            Class<? extends ClusterInvokeAcceptor> clusterInvokeAcceptorClass,
124                            Map<String, Serializable> context)
125                    throws Exception {
126    
127                    if (clusterInvokeAcceptorClass != null) {
128                            Constructor<? extends ClusterInvokeAcceptor> constructor =
129                                    clusterInvokeAcceptorClass.getDeclaredConstructor();
130    
131                            if (!constructor.isAccessible()) {
132                                    constructor.setAccessible(true);
133                            }
134    
135                            ClusterInvokeAcceptor clusterInvokeAcceptor =
136                                    constructor.newInstance();
137    
138                            if (!clusterInvokeAcceptor.accept(context)) {
139                                    return null;
140                            }
141                    }
142    
143                    if (servletContextName == null) {
144                            if (Validator.isNull(beanIdentifier)) {
145                                    return methodHandler.invoke(true);
146                            }
147                            else {
148                                    Object bean = PortalBeanLocatorUtil.locate(beanIdentifier);
149    
150                                    return methodHandler.invoke(bean);
151                            }
152                    }
153    
154                    ClassLoader contextClassLoader =
155                            ClassLoaderUtil.getContextClassLoader();
156    
157                    try {
158                            ClassLoader classLoader =
159                                    (ClassLoader)PortletBeanLocatorUtil.locate(
160                                            servletContextName, "portletClassLoader");
161    
162                            ClassLoaderUtil.setContextClassLoader(classLoader);
163    
164                            if (Validator.isNull(beanIdentifier)) {
165                                    return methodHandler.invoke(true);
166                            }
167                            else {
168                                    Object bean = PortletBeanLocatorUtil.locate(
169                                            servletContextName, beanIdentifier);
170    
171                                    return methodHandler.invoke(bean);
172                            }
173                    }
174                    finally {
175                            ClassLoaderUtil.setContextClassLoader(contextClassLoader);
176                    }
177            }
178    
179            private static Log _log = LogFactoryUtil.getLog(ClusterableAdvice.class);
180    
181            private static MethodKey _invokeMethodKey = new MethodKey(
182                    ClusterableAdvice.class, "_invoke", MethodHandler.class, String.class,
183                    String.class, Class.class, Map.class);
184    
185            private static Clusterable _nullClusterable = new Clusterable() {
186    
187                            public Class<? extends ClusterInvokeAcceptor> acceptor() {
188                                    return null;
189                            }
190    
191                            public Class<? extends Annotation> annotationType() {
192                                    return Clusterable.class;
193                            }
194    
195                    };
196    
197    }