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