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                    Object[] arguments = methodInvocation.getArguments();
161    
162                    ClassedModel classedModel = (ClassedModel)arguments[0];
163    
164                    long classPK = getClassPK(classedModel);
165    
166                    if (classPK == 0) {
167                            return;
168                    }
169    
170                    SystemEventHierarchyEntryThreadLocal.pop(
171                            getClassName(classedModel), classPK);
172            }
173    
174            @Override
175            public SystemEvent getNullAnnotation() {
176                    return _nullSystemEvent;
177            }
178    
179            protected String getClassName(ClassedModel classedModel) {
180                    String className = classedModel.getModelClassName();
181    
182                    if (classedModel instanceof StagedModel) {
183                            StagedModel stagedModel = (StagedModel)classedModel;
184    
185                            StagedModelType stagedModelType = stagedModel.getStagedModelType();
186    
187                            className = stagedModelType.getClassName();
188                    }
189    
190                    return className;
191            }
192    
193            protected long getClassPK(ClassedModel classedModel) {
194                    Serializable primaryKeyObj = classedModel.getPrimaryKeyObj();
195    
196                    if (!(primaryKeyObj instanceof Long)) {
197                            return 0;
198                    }
199    
200                    return (Long)primaryKeyObj;
201            }
202    
203            protected long getCompanyId(ClassedModel classedModel) {
204                    if (classedModel instanceof AuditedModel) {
205                            AuditedModel auditedModel = (AuditedModel)classedModel;
206    
207                            return auditedModel.getCompanyId();
208                    }
209    
210                    if (classedModel instanceof GroupedModel) {
211                            GroupedModel groupedModel = (GroupedModel)classedModel;
212    
213                            return groupedModel.getCompanyId();
214                    }
215    
216                    if (classedModel instanceof StagedModel) {
217                            StagedModel stagedModel = (StagedModel)classedModel;
218    
219                            return stagedModel.getCompanyId();
220                    }
221    
222                    return 0;
223            }
224    
225            protected long getGroupId(ClassedModel classedModel) {
226                    if (!(classedModel instanceof GroupedModel)) {
227                            return 0;
228                    }
229    
230                    GroupedModel groupedModel = (GroupedModel)classedModel;
231    
232                    return groupedModel.getGroupId();
233            }
234    
235            protected String getUuid(ClassedModel classedModel) throws Exception {
236                    String uuid = null;
237    
238                    if (classedModel instanceof StagedModel) {
239                            StagedModel stagedModel = (StagedModel)classedModel;
240    
241                            uuid = stagedModel.getUuid();
242                    }
243                    else {
244                            Class<?> modelClass = classedModel.getClass();
245    
246                            Method getUuidMethod = modelClass.getMethod(
247                                    "getUuid", new Class[0]);
248    
249                            if (getUuidMethod != null) {
250                                    uuid = (String)getUuidMethod.invoke(
251                                            classedModel, new Object[0]);
252                            }
253                            else {
254                                    uuid = StringPool.BLANK;
255                            }
256                    }
257    
258                    return uuid;
259            }
260    
261            protected boolean isValid(MethodInvocation methodInvocation, int phase) {
262                    Method method = methodInvocation.getMethod();
263    
264                    Class<?>[] parameterTypes = method.getParameterTypes();
265    
266                    if (parameterTypes.length == 0) {
267                            if (_log.isDebugEnabled() && (phase == PHASE_BEFORE)) {
268                                    _log.debug(
269                                            "The method " + methodInvocation +
270                                                    " must have at least one parameter");
271                            }
272    
273                            return false;
274                    }
275    
276                    Class<?> parameterType = parameterTypes[0];
277    
278                    if (!ClassedModel.class.isAssignableFrom(parameterType)) {
279                            if (_log.isDebugEnabled() && (phase == PHASE_BEFORE)) {
280                                    _log.debug(
281                                            "The first parameter of " + methodInvocation +
282                                                    " must implement ClassedModel");
283                            }
284    
285                            return false;
286                    }
287    
288                    Object[] arguments = methodInvocation.getArguments();
289    
290                    ClassedModel classedModel = (ClassedModel)arguments[0];
291    
292                    if ((classedModel == null) ||
293                            !(classedModel.getPrimaryKeyObj() instanceof Long)) {
294    
295                            if (_log.isDebugEnabled() && (phase == PHASE_BEFORE)) {
296                                    _log.debug(
297                                            "The first parameter of " + methodInvocation +
298                                                    " must be a long");
299                            }
300    
301                            return false;
302                    }
303    
304                    if (phase != PHASE_AFTER_RETURNING) {
305                            return true;
306                    }
307    
308                    if (!AuditedModel.class.isAssignableFrom(parameterType) &&
309                            !GroupedModel.class.isAssignableFrom(parameterType) &&
310                            !StagedModel.class.isAssignableFrom(parameterType)) {
311    
312                            if (_log.isDebugEnabled()) {
313                                    StringBundler sb = new StringBundler(4);
314    
315                                    sb.append("If send is true, the first parameter of ");
316                                    sb.append(methodInvocation);
317                                    sb.append(" must implement AuditedModel, GroupedModel, or ");
318                                    sb.append("StagedModel");
319    
320                                    _log.debug(sb.toString());
321                            }
322    
323                            return false;
324                    }
325    
326                    return true;
327            }
328    
329            private static final int PHASE_AFTER_RETURNING = 1;
330    
331            private static final int PHASE_BEFORE = 0;
332    
333            private static final int PHASE_DURING_FINALLY = 2;
334    
335            private static Log _log = LogFactoryUtil.getLog(SystemEventAdvice.class);
336    
337            private static SystemEvent _nullSystemEvent = new SystemEvent() {
338    
339                    @Override
340                    public Class<? extends Annotation> annotationType() {
341                            return SystemEvent.class;
342                    }
343    
344                    @Override
345                    public int action() {
346                            return SystemEventConstants.ACTION_NONE;
347                    }
348    
349                    @Override
350                    public boolean send() {
351                            return false;
352                    }
353    
354                    @Override
355                    public int type() {
356                            return 0;
357                    }
358    
359            };
360    
361    }