001    /**
002     * Copyright (c) 2000-2013 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                    Class<?> identifiableBeanClass = identifiableBean.getClass();
103    
104                    ClassLoader contextClassLoader = identifiableBeanClass.getClassLoader();
105    
106                    String servletContextName = ClassLoaderPool.getContextName(
107                            contextClassLoader);
108    
109                    Map<String, Serializable> context =
110                            ClusterableContextThreadLocal.collectThreadLocalContext();
111    
112                    return new MethodHandler(
113                            _invokeMethodKey, methodHandler, servletContextName,
114                            identifiableBean.getBeanIdentifier(), clusterInvokeAcceptorClass,
115                            context);
116            }
117    
118            @SuppressWarnings("unused")
119            private static Object _invoke(
120                            MethodHandler methodHandler, String servletContextName,
121                            String beanIdentifier,
122                            Class<? extends ClusterInvokeAcceptor> clusterInvokeAcceptorClass,
123                            Map<String, Serializable> context)
124                    throws Exception {
125    
126                    if (clusterInvokeAcceptorClass != null) {
127                            Constructor<? extends ClusterInvokeAcceptor> constructor =
128                                    clusterInvokeAcceptorClass.getDeclaredConstructor();
129    
130                            if (!constructor.isAccessible()) {
131                                    constructor.setAccessible(true);
132                            }
133    
134                            ClusterInvokeAcceptor clusterInvokeAcceptor =
135                                    constructor.newInstance();
136    
137                            if (!clusterInvokeAcceptor.accept(context)) {
138                                    return null;
139                            }
140                    }
141    
142                    if (Validator.isNull(servletContextName)) {
143                            if (Validator.isNull(beanIdentifier)) {
144                                    return methodHandler.invoke(true);
145                            }
146    
147                            Object bean = PortalBeanLocatorUtil.locate(beanIdentifier);
148    
149                            return methodHandler.invoke(bean);
150                    }
151    
152                    ClassLoader contextClassLoader =
153                            ClassLoaderUtil.getContextClassLoader();
154    
155                    try {
156                            ClassLoader classLoader = ClassLoaderPool.getClassLoader(
157                                    servletContextName);
158    
159                            ClassLoaderUtil.setContextClassLoader(classLoader);
160    
161                            if (Validator.isNull(beanIdentifier)) {
162                                    return methodHandler.invoke(true);
163                            }
164    
165                            Object bean = PortletBeanLocatorUtil.locate(
166                                    servletContextName, beanIdentifier);
167    
168                            return methodHandler.invoke(bean);
169                    }
170                    finally {
171                            ClassLoaderUtil.setContextClassLoader(contextClassLoader);
172                    }
173            }
174    
175            private static Log _log = LogFactoryUtil.getLog(ClusterableAdvice.class);
176    
177            private static MethodKey _invokeMethodKey = new MethodKey(
178                    ClusterableAdvice.class, "_invoke", MethodHandler.class, String.class,
179                    String.class, Class.class, Map.class);
180    
181            private static Clusterable _nullClusterable = new Clusterable() {
182    
183                            @Override
184                            public Class<? extends ClusterInvokeAcceptor> acceptor() {
185                                    return null;
186                            }
187    
188                            @Override
189                            public Class<? extends Annotation> annotationType() {
190                                    return Clusterable.class;
191                            }
192    
193                    };
194    
195    }