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