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.lar.backgroundtask;
016    
017    import com.liferay.portal.kernel.backgroundtask.BackgroundTaskResult;
018    import com.liferay.portal.kernel.backgroundtask.BaseBackgroundTaskExecutor;
019    import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
020    import com.liferay.portal.kernel.dao.orm.DynamicQuery;
021    import com.liferay.portal.kernel.dao.orm.Property;
022    import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
023    import com.liferay.portal.kernel.exception.PortalException;
024    import com.liferay.portal.kernel.exception.SystemException;
025    import com.liferay.portal.kernel.lar.PortletDataContext;
026    import com.liferay.portal.kernel.lar.PortletDataHandlerKeys;
027    import com.liferay.portal.kernel.log.Log;
028    import com.liferay.portal.kernel.log.LogFactoryUtil;
029    import com.liferay.portal.kernel.search.Indexer;
030    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
031    import com.liferay.portal.kernel.search.SearchException;
032    import com.liferay.portal.kernel.util.ArrayUtil;
033    import com.liferay.portal.kernel.util.GetterUtil;
034    import com.liferay.portal.kernel.util.MapUtil;
035    import com.liferay.portal.kernel.util.ReflectionUtil;
036    import com.liferay.portal.kernel.util.StringBundler;
037    import com.liferay.portal.kernel.util.StringPool;
038    import com.liferay.portal.model.BackgroundTask;
039    import com.liferay.portal.model.User;
040    import com.liferay.portlet.documentlibrary.model.DLFileEntry;
041    import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
042    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
043    import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLocalServiceUtil;
044    import com.liferay.portlet.journal.model.JournalArticle;
045    import com.liferay.portlet.journal.service.persistence.JournalArticleActionableDynamicQuery;
046    import com.liferay.portlet.journal.util.JournalArticleIndexer;
047    
048    import java.io.Serializable;
049    
050    import java.lang.reflect.Method;
051    
052    import java.util.ArrayList;
053    import java.util.HashSet;
054    import java.util.List;
055    import java.util.Map;
056    import java.util.Set;
057    
058    /**
059     * @author Mate Thurzo
060     */
061    public class StagingIndexingBackgroundTaskExecutor
062            extends BaseBackgroundTaskExecutor {
063    
064            public StagingIndexingBackgroundTaskExecutor() {
065                    setSerial(true);
066            }
067    
068            @Override
069            public BackgroundTaskResult execute(BackgroundTask backgroundTask)
070                    throws Exception {
071    
072                    Map<String, Serializable> taskContextMap =
073                            backgroundTask.getTaskContextMap();
074    
075                    PortletDataContext portletDataContext =
076                            (PortletDataContext)taskContextMap.get("portletDataContext");
077    
078                    try {
079                            deleteDocuments(portletDataContext);
080                    }
081                    catch (Exception e) {
082                            _log.error("Unable to delete documents", e);
083                    }
084    
085                    boolean importPermissions = MapUtil.getBoolean(
086                            portletDataContext.getParameterMap(),
087                            PortletDataHandlerKeys.PERMISSIONS);
088    
089                    if (importPermissions) {
090                            long userId = MapUtil.getLong(taskContextMap, "userId");
091    
092                            if (userId > 0) {
093                                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
094                                            User.class);
095    
096                                    indexer.reindex(userId);
097                            }
098                    }
099    
100                    Map<String, Map<?, ?>> newPrimaryKeysMaps =
101                            portletDataContext.getNewPrimaryKeysMaps();
102    
103                    for (Map.Entry<String, Map<?, ?>> newPrimaryKeysMapsEntry :
104                                    newPrimaryKeysMaps.entrySet()) {
105    
106                            String className = newPrimaryKeysMapsEntry.getKey();
107    
108                            Indexer indexer = IndexerRegistryUtil.getIndexer(className);
109    
110                            if ((indexer == null) &&
111                                    !className.equals(DDMStructure.class.getName())) {
112    
113                                    continue;
114                            }
115    
116                            Map<?, ?> newPrimaryKeysMap = newPrimaryKeysMapsEntry.getValue();
117    
118                            List<Long> newPrimaryKeys = new ArrayList<Long>();
119    
120                            for (Object object : newPrimaryKeysMap.values()) {
121                                    long classPK = GetterUtil.getLong(object);
122    
123                                    if (classPK > 0) {
124                                            newPrimaryKeys.add(classPK);
125                                    }
126                            }
127    
128                            if (className.equals(DDMStructure.class.getName())) {
129                                    reindexDDMStructures(
130                                            newPrimaryKeys, newPrimaryKeysMaps,
131                                            portletDataContext.getGroupId());
132                            }
133                            else {
134                                    for (Long classPK : newPrimaryKeys) {
135                                            indexer.reindex(className, classPK);
136                                    }
137                            }
138                    }
139    
140                    return BackgroundTaskResult.SUCCESS;
141            }
142    
143            protected void deleteDocuments(PortletDataContext portletDataContext) {
144                    Map<String, List<String>> deletionKeysMap =
145                            portletDataContext.getDeletionKeysMap();
146    
147                    for (String className : deletionKeysMap.keySet()) {
148                            Indexer indexer = IndexerRegistryUtil.getIndexer(className);
149    
150                            List<String> uids = deletionKeysMap.get(className);
151    
152                            for (String uid : uids) {
153                                    try {
154                                            indexer.delete(portletDataContext.getCompanyId(), uid);
155                                    }
156                                    catch (SearchException se) {
157                                            _log.error("Unable to delete document with UID " + uid, se);
158                                    }
159                            }
160                    }
161            }
162    
163            protected ActionableDynamicQuery getJournalArticleActionableDynamicQuery(
164                            long groupId, final Map<?, ?> journalArticleIds,
165                            final String[] ddmStructureKeys)
166                    throws Exception {
167    
168                    ActionableDynamicQuery journalArticleActionableDynamicQuery =
169                            new JournalArticleActionableDynamicQuery() {
170    
171                            @Override
172                            protected void addCriteria(DynamicQuery dynamicQuery) {
173                                    Property structureIdProperty = PropertyFactoryUtil.forName(
174                                            "structureId");
175    
176                                    dynamicQuery.add(structureIdProperty.in(ddmStructureKeys));
177                            }
178    
179                            @Override
180                            protected void performAction(Object object) throws PortalException {
181                                    JournalArticle article = (JournalArticle)object;
182    
183                                    if (containsValue(
184                                                    journalArticleIds, article.getResourcePrimKey())) {
185    
186                                            return;
187                                    }
188    
189                                    try {
190                                            _journalArticleIndexer.doReindex(article, false);
191                                    }
192                                    catch (Exception e) {
193                                            throw new PortalException(e);
194                                    }
195                            }
196    
197                            private final JournalArticleIndexer _journalArticleIndexer =
198                                    (JournalArticleIndexer)IndexerRegistryUtil.getIndexer(
199                                            JournalArticle.class);
200    
201                    };
202    
203                    journalArticleActionableDynamicQuery.setGroupId(groupId);
204    
205                    return journalArticleActionableDynamicQuery;
206            }
207    
208            protected void reindexDDMStructures(
209                            List<Long> ddmStructureIds,
210                            final Map<String, Map<?, ?>> newPrimaryKeysMaps, long groupId)
211                    throws Exception {
212    
213                    if ((ddmStructureIds == null) || ddmStructureIds.isEmpty()) {
214                            return;
215                    }
216    
217                    Set<Long> descendantDDMStructureIds = new HashSet<Long>();
218    
219                    for (long structureId : ddmStructureIds) {
220                            DDMStructure ddmStructure =
221                                    DDMStructureLocalServiceUtil.getDDMStructure(structureId);
222    
223                            descendantDDMStructureIds.addAll(
224                                    getDescendantDDMStructureIds(
225                                            ddmStructure.getGroupId(), ddmStructure.getStructureId()));
226                    }
227    
228                    int i = 0;
229    
230                    String[] ddmStructureKeys =
231                            new String[descendantDDMStructureIds.size()];
232    
233                    for (long ddmStructureId : descendantDDMStructureIds) {
234                            DDMStructure ddmStructure =
235                                    DDMStructureLocalServiceUtil.getDDMStructure(ddmStructureId);
236    
237                            ddmStructureKeys[i++] = ddmStructure.getStructureKey();
238                    }
239    
240                    // Journal
241    
242                    Map<?, ?> articleIds = (Map<?, ?>)newPrimaryKeysMaps.get(
243                            JournalArticle.class.getName());
244    
245                    ActionableDynamicQuery journalArticleActionableDynamicQuery =
246                            getJournalArticleActionableDynamicQuery(
247                                    groupId, articleIds, ddmStructureKeys);
248    
249                    journalArticleActionableDynamicQuery.performActions();
250    
251                    // Document library
252    
253                    List<DLFileEntry> dlFileEntries = new ArrayList<DLFileEntry>();
254    
255                    try {
256                            Method method = ReflectionUtil.getDeclaredMethod(
257                                    DLFileEntryLocalServiceUtil.class, "getDDMStructureFileEntries",
258                                    long.class, long[].class);
259    
260                            Object object = method.invoke(
261                                    DLFileEntryLocalServiceUtil.class, groupId,
262                                    ArrayUtil.toLongArray(descendantDDMStructureIds));
263    
264                            if (object != null) {
265                                    dlFileEntries = (List<DLFileEntry>)object;
266                            }
267                    }
268                    catch (Exception e) {
269                            List<DLFileEntry> allDlFileEntries =
270                                    DLFileEntryLocalServiceUtil.getDDMStructureFileEntries(
271                                            ArrayUtil.toLongArray(descendantDDMStructureIds));
272    
273                            for (DLFileEntry dlFileEntry : allDlFileEntries) {
274                                    if (groupId == dlFileEntry.getGroupId()) {
275                                            dlFileEntries.add(dlFileEntry);
276                                    }
277                            }
278                    }
279    
280                    Map<?, ?> dlFileEntryPrimaryKeysMap = newPrimaryKeysMaps.get(
281                            DLFileEntry.class.getName());
282    
283                    Indexer dlFileEntryIndexer = IndexerRegistryUtil.getIndexer(
284                            DLFileEntry.class);
285    
286                    for (DLFileEntry dlFileEntry : dlFileEntries) {
287                            if (containsValue(
288                                            dlFileEntryPrimaryKeysMap, dlFileEntry.getFileEntryId())) {
289    
290                                    continue;
291                            }
292    
293                            dlFileEntryIndexer.reindex(dlFileEntry);
294                    }
295            }
296    
297            protected boolean containsValue(Map<?, ?> map, long value) {
298                    if ((map == null) || map.isEmpty() || (value <= 0)) {
299                            return false;
300                    }
301    
302                    for (Object object : map.values()) {
303                            if (GetterUtil.getLong(object) == value) {
304                                    return true;
305                            }
306                    }
307    
308                    return false;
309            }
310    
311            protected void getDescendantDDMStructureIds(
312                            List<Long> ddmStructureIds, long groupId, long parentDDMStructureId)
313                    throws PortalException, SystemException {
314    
315                    DynamicQuery query = DDMStructureLocalServiceUtil.dynamicQuery();
316    
317                    Property groupProperty = PropertyFactoryUtil.forName("groupId");
318    
319                    query.add(groupProperty.eq(groupId));
320    
321                    Property parentStructureIdProperty = PropertyFactoryUtil.forName(
322                            "parentStructureId");
323    
324                    query.add(parentStructureIdProperty.eq(parentDDMStructureId));
325    
326                    List<DDMStructure> ddmStructures =
327                                    DDMStructureLocalServiceUtil.dynamicQuery(query);
328    
329                    for (DDMStructure ddmStructure : ddmStructures) {
330                            ddmStructureIds.add(ddmStructure.getStructureId());
331    
332                            getDescendantDDMStructureIds(
333                                    ddmStructureIds, ddmStructure.getGroupId(),
334                                    ddmStructure.getStructureId());
335                    }
336            }
337    
338            protected List<Long> getDescendantDDMStructureIds(
339                            long groupId, long ddmStructureId)
340                    throws PortalException, SystemException {
341    
342                    List<Long> ddmStructureIds = new ArrayList<Long>();
343    
344                    getDescendantDDMStructureIds(ddmStructureIds, groupId, ddmStructureId);
345    
346                    ddmStructureIds.add(0, ddmStructureId);
347    
348                    return ddmStructureIds;
349            }
350    
351            @Override
352            public String handleException(BackgroundTask backgroundTask, Exception e) {
353                    StringBundler sb = new StringBundler(4);
354    
355                    sb.append("Indexing failed after importing with the following error: ");
356                    sb.append(e.getMessage());
357                    sb.append(StringPool.PERIOD);
358                    sb.append(StringPool.SPACE);
359                    sb.append("Please reindex site manually.");
360    
361                    String message = sb.toString();
362    
363                    if (_log.isInfoEnabled()) {
364                            _log.info(message);
365                    }
366    
367                    return message;
368            }
369    
370            private static Log _log = LogFactoryUtil.getLog(
371                    StagingIndexingBackgroundTaskExecutor.class);
372    
373    }