001    /**
002     * Copyright (c) 2000-present 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.kernel.test.rule.callback;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.test.rule.DeleteAfterTestRun;
020    import com.liferay.portal.kernel.util.ArrayUtil;
021    import com.liferay.portal.kernel.util.StringBundler;
022    import com.liferay.portal.model.Company;
023    import com.liferay.portal.model.Group;
024    import com.liferay.portal.model.LayoutPrototype;
025    import com.liferay.portal.model.LayoutSetPrototype;
026    import com.liferay.portal.model.Organization;
027    import com.liferay.portal.model.PersistedModel;
028    import com.liferay.portal.model.Role;
029    import com.liferay.portal.model.User;
030    import com.liferay.portal.model.UserGroup;
031    import com.liferay.portal.service.PersistedModelLocalService;
032    import com.liferay.portal.service.PersistedModelLocalServiceRegistryUtil;
033    
034    import java.lang.reflect.Field;
035    
036    import java.util.ArrayList;
037    import java.util.Arrays;
038    import java.util.Collection;
039    import java.util.HashMap;
040    import java.util.Iterator;
041    import java.util.LinkedHashSet;
042    import java.util.LinkedList;
043    import java.util.List;
044    import java.util.Map;
045    import java.util.Queue;
046    import java.util.Set;
047    
048    import org.junit.runner.Description;
049    
050    /**
051     * @author Shuyang Zhou
052     */
053    public class DeleteAfterTestRunTestCallback
054            extends BaseTestCallback<Object, Object> {
055    
056            public static final DeleteAfterTestRunTestCallback INSTANCE =
057                    new DeleteAfterTestRunTestCallback();
058    
059            @Override
060            public void afterMethod(
061                    Description description, Object object, Object target) {
062    
063                    Class<?> testClass = description.getTestClass();
064    
065                    Map<Class<?>, FieldBag> deleteAfterTestRunFieldBags = new HashMap<>();
066    
067                    while (testClass != null) {
068                            for (Field field : testClass.getDeclaredFields()) {
069                                    DeleteAfterTestRun deleteAfterTestRun = field.getAnnotation(
070                                            DeleteAfterTestRun.class);
071    
072                                    if (deleteAfterTestRun == null) {
073                                            continue;
074                                    }
075    
076                                    Class<?> fieldClass = field.getType();
077    
078                                    if (PersistedModel.class.isAssignableFrom(fieldClass)) {
079                                            addField(deleteAfterTestRunFieldBags, fieldClass, field);
080    
081                                            continue;
082                                    }
083    
084                                    if (fieldClass.isArray()) {
085                                            if (!PersistedModel.class.isAssignableFrom(
086                                                            fieldClass.getComponentType())) {
087    
088                                                    throw new IllegalArgumentException(
089                                                            "Unable to annotate field " + field +
090                                                                    " because it is not an array of type " +
091                                                                            PersistedModel.class.getName());
092                                            }
093    
094                                            addField(
095                                                    deleteAfterTestRunFieldBags,
096                                                    fieldClass.getComponentType(), field);
097    
098                                            continue;
099                                    }
100    
101                                    if (Collection.class.isAssignableFrom(fieldClass)) {
102                                            try {
103                                                    field.setAccessible(true);
104    
105                                                    Collection<?> collection = (Collection<?>)field.get(
106                                                            target);
107    
108                                                    if ((collection == null) || collection.isEmpty()) {
109                                                            continue;
110                                                    }
111    
112                                                    Class<?> collectionType = getCollectionType(collection);
113    
114                                                    if (collectionType == null) {
115                                                            throw new IllegalArgumentException(
116                                                                    "Unable to annotate field " + field +
117                                                                            " because it is not a collection of type " +
118                                                                                    PersistedModel.class.getName());
119                                                    }
120    
121                                                    addField(
122                                                            deleteAfterTestRunFieldBags, collectionType, field);
123                                            }
124                                            catch (Exception e) {
125                                                    _log.error(
126                                                            "Unable to detect collection element type", e);
127                                            }
128    
129                                            continue;
130                                    }
131    
132                                    StringBundler sb = new StringBundler(6);
133    
134                                    sb.append("Unable to annotate field ");
135                                    sb.append(field);
136                                    sb.append(" because it is not type of ");
137                                    sb.append(PersistedModel.class.getName());
138                                    sb.append(" nor an array or collection of ");
139                                    sb.append(PersistedModel.class.getName());
140    
141                                    throw new IllegalArgumentException(sb.toString());
142                            }
143    
144                            testClass = testClass.getSuperclass();
145                    }
146    
147                    Set<Map.Entry<Class<?>, FieldBag>> set =
148                            deleteAfterTestRunFieldBags.entrySet();
149    
150                    Iterator<Map.Entry<Class<?>, FieldBag>> iterator = set.iterator();
151    
152                    while (iterator.hasNext()) {
153                            Map.Entry<Class<?>, FieldBag> entry = iterator.next();
154    
155                            Class<?> clazz = entry.getKey();
156    
157                            if (_orderedClasses.contains(clazz)) {
158                                    continue;
159                            }
160    
161                            iterator.remove();
162    
163                            removeField(entry.getValue(), target);
164                    }
165    
166                    for (Class<?> clazz : _orderedClasses) {
167                            FieldBag fieldBag = deleteAfterTestRunFieldBags.remove(clazz);
168    
169                            if (fieldBag == null) {
170                                    continue;
171                            }
172    
173                            removeField(fieldBag, target);
174                    }
175            }
176    
177            protected void addField(
178                    Map<Class<?>, FieldBag> deleteAfterTestRunFieldBags, Class<?> clazz,
179                    Field field) {
180    
181                    FieldBag fieldBag = deleteAfterTestRunFieldBags.get(clazz);
182    
183                    if (fieldBag == null) {
184                            fieldBag = new FieldBag(clazz);
185    
186                            deleteAfterTestRunFieldBags.put(clazz, fieldBag);
187                    }
188    
189                    field.setAccessible(true);
190    
191                    fieldBag.addField(field);
192            }
193    
194            protected Class<? extends PersistedModel> getCollectionType(
195                    Collection<?> collection) {
196    
197                    Class<? extends PersistedModel> collectionType = null;
198    
199                    for (Object object : collection) {
200                            Queue<Class<?>> classes = new LinkedList<>();
201    
202                            classes.add(object.getClass());
203    
204                            Class<?> clazz = null;
205    
206                            while ((clazz = classes.poll()) != null) {
207                                    if (ArrayUtil.contains(
208                                                    clazz.getInterfaces(), PersistedModel.class)) {
209    
210                                            if (collectionType == null) {
211                                                    collectionType = (Class<? extends PersistedModel>)clazz;
212                                            }
213                                            else if (collectionType != clazz) {
214                                                    return null;
215                                            }
216    
217                                            break;
218                                    }
219    
220                                    classes.add(clazz.getSuperclass());
221                                    classes.addAll(Arrays.asList(clazz.getInterfaces()));
222                            }
223                    }
224    
225                    return collectionType;
226            }
227    
228            protected void removeField(FieldBag fieldBag, Object instance) {
229                    try {
230                            Class<?> fieldClass = fieldBag.getFieldClass();
231    
232                            PersistedModelLocalService persistedModelLocalService =
233                                    PersistedModelLocalServiceRegistryUtil.
234                                            getPersistedModelLocalService(fieldClass.getName());
235    
236                            for (Field field : fieldBag.getFields()) {
237                                    Object object = field.get(instance);
238    
239                                    if (object == null) {
240                                            continue;
241                                    }
242    
243                                    Class<?> objectClass = object.getClass();
244    
245                                    if (objectClass.isArray()) {
246                                            for (PersistedModel persistedModel :
247                                                            (PersistedModel[])object) {
248    
249                                                    if (persistedModel == null) {
250                                                            continue;
251                                                    }
252    
253                                                    persistedModelLocalService.deletePersistedModel(
254                                                            persistedModel);
255                                            }
256                                    }
257                                    else if (Collection.class.isAssignableFrom(objectClass)) {
258                                            Collection<? extends PersistedModel> collection =
259                                                    (Collection<? extends PersistedModel>)object;
260    
261                                            for (PersistedModel persistedModel : collection) {
262                                                    persistedModelLocalService.deletePersistedModel(
263                                                            persistedModel);
264                                            }
265                                    }
266                                    else {
267                                            persistedModelLocalService.deletePersistedModel(
268                                                    (PersistedModel)object);
269                                    }
270    
271                                    field.set(instance, null);
272                            }
273                    }
274                    catch (Exception e) {
275                            _log.error("Unable to delete", e);
276                    }
277            }
278    
279            protected static class FieldBag {
280    
281                    public FieldBag(Class<?> fieldClass) {
282                            _fieldClass = fieldClass;
283                    }
284    
285                    public void addField(Field field) {
286                            _fields.add(field);
287                    }
288    
289                    public Class<?> getFieldClass() {
290                            return _fieldClass;
291                    }
292    
293                    public List<Field> getFields() {
294                            return _fields;
295                    }
296    
297                    private final Class<?> _fieldClass;
298                    private final List<Field> _fields = new ArrayList<>();
299    
300            }
301    
302            private DeleteAfterTestRunTestCallback() {
303            }
304    
305            private static final Log _log = LogFactoryUtil.getLog(
306                    DeleteAfterTestRunTestCallback.class);
307    
308            private static final Set<Class<?>> _orderedClasses = new LinkedHashSet<>(
309                    Arrays.<Class<?>>asList(
310                            User.class, Organization.class, Role.class, UserGroup.class,
311                            Group.class, LayoutPrototype.class, LayoutSetPrototype.class,
312                            Company.class));
313    
314    }