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.systemevent;
016    
017    import com.liferay.portal.kernel.lar.StagedModelType;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.systemevent.SystemEvent;
021    import com.liferay.portal.kernel.systemevent.SystemEventHierarchyEntry;
022    import com.liferay.portal.kernel.systemevent.SystemEventHierarchyEntryThreadLocal;
023    import com.liferay.portal.kernel.util.StringBundler;
024    import com.liferay.portal.kernel.util.StringPool;
025    import com.liferay.portal.model.AuditedModel;
026    import com.liferay.portal.model.ClassedModel;
027    import com.liferay.portal.model.GroupedModel;
028    import com.liferay.portal.model.StagedModel;
029    import com.liferay.portal.model.SystemEventConstants;
030    import com.liferay.portal.model.TypedModel;
031    import com.liferay.portal.service.SystemEventLocalServiceUtil;
032    import com.liferay.portal.spring.aop.AnnotationChainableMethodAdvice;
033    
034    import java.io.Serializable;
035    
036    import java.lang.annotation.Annotation;
037    import java.lang.reflect.Method;
038    
039    import org.aopalliance.intercept.MethodInvocation;
040    
041    /**
042     * @author Zsolt Berentey
043     */
044    public class SystemEventAdvice
045            extends AnnotationChainableMethodAdvice<SystemEvent> {
046    
047            @Override
048            public void afterReturning(MethodInvocation methodInvocation, Object result)
049                    throws Throwable {
050    
051                    SystemEvent systemEvent = findAnnotation(methodInvocation);
052    
053                    if ((systemEvent == _nullSystemEvent) || !systemEvent.send()) {
054                            return;
055                    }
056    
057                    if (!isValid(methodInvocation, PHASE_AFTER_RETURNING)) {
058                            return;
059                    }
060    
061                    Object[] arguments = methodInvocation.getArguments();
062    
063                    ClassedModel classedModel = (ClassedModel)arguments[0];
064    
065                    long groupId = getGroupId(classedModel);
066    
067                    String className = getClassName(classedModel);
068    
069                    String referrerClassName = null;
070    
071                    if (classedModel instanceof TypedModel) {
072                            TypedModel typedModel = (TypedModel)classedModel;
073    
074                            referrerClassName = typedModel.getClassName();
075                    }
076    
077                    long classPK = getClassPK(classedModel);
078    
079                    SystemEventHierarchyEntry systemEventHierarchyEntry =
080                            SystemEventHierarchyEntryThreadLocal.peek();
081    
082                    if ((systemEventHierarchyEntry != null) &&
083                            systemEventHierarchyEntry.hasTypedModel(className, classPK)) {
084    
085                            if (groupId > 0) {
086                                    SystemEventLocalServiceUtil.addSystemEvent(
087                                            0, groupId, systemEventHierarchyEntry.getClassName(),
088                                            classPK, systemEventHierarchyEntry.getUuid(),
089                                            referrerClassName, systemEvent.type(),
090                                            systemEventHierarchyEntry.getExtraData());
091                            }
092                            else {
093                                    SystemEventLocalServiceUtil.addSystemEvent(
094                                            getCompanyId(classedModel),
095                                            systemEventHierarchyEntry.getClassName(), classPK,
096                                            systemEventHierarchyEntry.getUuid(), referrerClassName,
097                                            systemEvent.type(),
098                                            systemEventHierarchyEntry.getExtraData());
099                            }
100                    }
101                    else if (groupId > 0) {
102                            SystemEventLocalServiceUtil.addSystemEvent(
103                                    0, groupId, className, classPK, getUuid(classedModel),
104                                    referrerClassName, systemEvent.type(), StringPool.BLANK);
105                    }
106                    else {
107                            SystemEventLocalServiceUtil.addSystemEvent(
108                                    getCompanyId(classedModel), className, classPK,
109                                    getUuid(classedModel), referrerClassName, systemEvent.type(),
110                                    StringPool.BLANK);
111                    }
112            }
113    
114            @Override
115            public Object before(MethodInvocation methodInvocation) throws Throwable {
116                    SystemEvent systemEvent = findAnnotation(methodInvocation);
117    
118                    if (systemEvent == _nullSystemEvent) {
119                            return null;
120                    }
121    
122                    if (systemEvent.action() != SystemEventConstants.ACTION_NONE) {
123                            if (!isValid(methodInvocation, PHASE_BEFORE)) {
124                                    return null;
125                            }
126    
127                            Object[] arguments = methodInvocation.getArguments();
128    
129                            ClassedModel classedModel = (ClassedModel)arguments[0];
130    
131                            SystemEventHierarchyEntry systemEventHierarchyEntry =
132                                    SystemEventHierarchyEntryThreadLocal.push(
133                                            getClassName(classedModel), getClassPK(classedModel),
134                                            systemEvent.action());
135    
136                            if (systemEventHierarchyEntry != null) {
137                                    systemEventHierarchyEntry.setUuid(getUuid(classedModel));
138                            }
139                    }
140    
141                    return null;
142            }
143    
144            @Override
145            public void duringFinally(MethodInvocation methodInvocation) {
146                    SystemEvent systemEvent = findAnnotation(methodInvocation);
147    
148                    if (systemEvent == _nullSystemEvent) {
149                            return;
150                    }
151    
152                    if (!isValid(methodInvocation, PHASE_DURING_FINALLY)) {
153                            return;
154                    }
155    
156                    if (systemEvent.action() == SystemEventConstants.ACTION_NONE) {
157                            return;
158                    }
159    
160                    SystemEventHierarchyEntry systemEventHierarchyEntry =
161                            SystemEventHierarchyEntryThreadLocal.peek();
162    
163                    if (systemEventHierarchyEntry == null) {
164                            return;
165                    }
166    
167                    Object[] arguments = methodInvocation.getArguments();
168    
169                    ClassedModel classedModel = (ClassedModel)arguments[0];
170    
171                    long classPK = getClassPK(classedModel);
172    
173                    if (classPK == 0) {
174                            return;
175                    }
176    
177                    if (systemEventHierarchyEntry.hasTypedModel(
178                                    getClassName(classedModel), classPK)) {
179    
180                            SystemEventHierarchyEntryThreadLocal.pop();
181                    }
182            }
183    
184            @Override
185            public SystemEvent getNullAnnotation() {
186                    return _nullSystemEvent;
187            }
188    
189            protected String getClassName(ClassedModel classedModel) {
190                    String className = classedModel.getModelClassName();
191    
192                    if (classedModel instanceof StagedModel) {
193                            StagedModel stagedModel = (StagedModel)classedModel;
194    
195                            StagedModelType stagedModelType = stagedModel.getStagedModelType();
196    
197                            className = stagedModelType.getClassName();
198                    }
199    
200                    return className;
201            }
202    
203            protected long getClassPK(ClassedModel classedModel) {
204                    Serializable primaryKeyObj = classedModel.getPrimaryKeyObj();
205    
206                    if (!(primaryKeyObj instanceof Long)) {
207                            return 0;
208                    }
209    
210                    return (Long)primaryKeyObj;
211            }
212    
213            protected long getCompanyId(ClassedModel classedModel) {
214                    if (classedModel instanceof AuditedModel) {
215                            AuditedModel auditedModel = (AuditedModel)classedModel;
216    
217                            return auditedModel.getCompanyId();
218                    }
219    
220                    if (classedModel instanceof GroupedModel) {
221                            GroupedModel groupedModel = (GroupedModel)classedModel;
222    
223                            return groupedModel.getCompanyId();
224                    }
225    
226                    if (classedModel instanceof StagedModel) {
227                            StagedModel stagedModel = (StagedModel)classedModel;
228    
229                            return stagedModel.getCompanyId();
230                    }
231    
232                    return 0;
233            }
234    
235            protected long getGroupId(ClassedModel classedModel) {
236                    if (!(classedModel instanceof GroupedModel)) {
237                            return 0;
238                    }
239    
240                    GroupedModel groupedModel = (GroupedModel)classedModel;
241    
242                    return groupedModel.getGroupId();
243            }
244    
245            protected String getUuid(ClassedModel classedModel) throws Exception {
246                    String uuid = null;
247    
248                    if (classedModel instanceof StagedModel) {
249                            StagedModel stagedModel = (StagedModel)classedModel;
250    
251                            uuid = stagedModel.getUuid();
252                    }
253                    else {
254                            Class<?> modelClass = classedModel.getClass();
255    
256                            Method getUuidMethod = modelClass.getMethod(
257                                    "getUuid", new Class[0]);
258    
259                            if (getUuidMethod != null) {
260                                    uuid = (String)getUuidMethod.invoke(
261                                            classedModel, new Object[0]);
262                            }
263                            else {
264                                    uuid = StringPool.BLANK;
265                            }
266                    }
267    
268                    return uuid;
269            }
270    
271            protected boolean isValid(MethodInvocation methodInvocation, int phase) {
272                    Method method = methodInvocation.getMethod();
273    
274                    Class<?>[] parameterTypes = method.getParameterTypes();
275    
276                    if (parameterTypes.length == 0) {
277                            if (_log.isDebugEnabled() && (phase == PHASE_BEFORE)) {
278                                    _log.debug(
279                                            "The method " + methodInvocation +
280                                                    " must have at least one parameter");
281                            }
282    
283                            return false;
284                    }
285    
286                    Class<?> parameterType = parameterTypes[0];
287    
288                    if (!ClassedModel.class.isAssignableFrom(parameterType)) {
289                            if (_log.isDebugEnabled() && (phase == PHASE_BEFORE)) {
290                                    _log.debug(
291                                            "The first parameter of " + methodInvocation +
292                                                    " must implement ClassedModel");
293                            }
294    
295                            return false;
296                    }
297    
298                    Object[] arguments = methodInvocation.getArguments();
299    
300                    ClassedModel classedModel = (ClassedModel)arguments[0];
301    
302                    if (!(classedModel.getPrimaryKeyObj() instanceof Long)) {
303                            if (_log.isDebugEnabled() && (phase == PHASE_BEFORE)) {
304                                    _log.debug(
305                                            "The first parameter of " + methodInvocation +
306                                                    " must be a long");
307                            }
308    
309                            return false;
310                    }
311    
312                    if (phase != PHASE_AFTER_RETURNING) {
313                            return true;
314                    }
315    
316                    if (!AuditedModel.class.isAssignableFrom(parameterType) &&
317                            !GroupedModel.class.isAssignableFrom(parameterType) &&
318                            !StagedModel.class.isAssignableFrom(parameterType)) {
319    
320                            if (_log.isDebugEnabled()) {
321                                    StringBundler sb = new StringBundler(4);
322    
323                                    sb.append("If send is true, the first parameter of ");
324                                    sb.append(methodInvocation);
325                                    sb.append(" must implement AuditedModel, GroupedModel, or ");
326                                    sb.append("StagedModel");
327    
328                                    _log.debug(sb.toString());
329                            }
330    
331                            return false;
332                    }
333    
334                    return true;
335            }
336    
337            private static final int PHASE_AFTER_RETURNING = 1;
338    
339            private static final int PHASE_BEFORE = 0;
340    
341            private static final int PHASE_DURING_FINALLY = 2;
342    
343            private static Log _log = LogFactoryUtil.getLog(SystemEventAdvice.class);
344    
345            private static SystemEvent _nullSystemEvent = new SystemEvent() {
346    
347                    @Override
348                    public Class<? extends Annotation> annotationType() {
349                            return SystemEvent.class;
350                    }
351    
352                    @Override
353                    public int action() {
354                            return SystemEventConstants.ACTION_NONE;
355                    }
356    
357                    @Override
358                    public boolean send() {
359                            return false;
360                    }
361    
362                    @Override
363                    public int type() {
364                            return 0;
365                    }
366    
367            };
368    
369    }