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.dao.orm;
016    
017    import com.liferay.portal.kernel.concurrent.ThreadPoolExecutor;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.exception.SystemException;
020    import com.liferay.portal.kernel.executor.PortalExecutorManagerUtil;
021    import com.liferay.portal.kernel.search.Document;
022    import com.liferay.portal.kernel.search.Indexer;
023    import com.liferay.portal.kernel.search.SearchEngineUtil;
024    import com.liferay.portal.kernel.transaction.Propagation;
025    import com.liferay.portal.kernel.transaction.TransactionAttribute;
026    import com.liferay.portal.kernel.transaction.TransactionInvokerUtil;
027    import com.liferay.portal.kernel.util.Validator;
028    import com.liferay.portal.model.BaseModel;
029    import com.liferay.portal.service.BaseLocalService;
030    
031    import java.lang.reflect.InvocationTargetException;
032    import java.lang.reflect.Method;
033    
034    import java.util.ArrayList;
035    import java.util.Collection;
036    import java.util.List;
037    import java.util.concurrent.Callable;
038    import java.util.concurrent.Future;
039    
040    /**
041     * @author Brian Wing Shun Chan
042     * @author Shuyang Zhou
043     */
044    public abstract class BaseActionableDynamicQuery
045            implements ActionableDynamicQuery {
046    
047            public static final TransactionAttribute REQUIRES_NEW_TRANSACTION_ATTRIBUTE;
048    
049            static {
050                    TransactionAttribute.Builder builder =
051                            new TransactionAttribute.Builder();
052    
053                    builder.setPropagation(Propagation.REQUIRES_NEW);
054                    builder.setRollbackForClasses(
055                            PortalException.class, SystemException.class);
056    
057                    REQUIRES_NEW_TRANSACTION_ATTRIBUTE = builder.build();
058            }
059    
060            @Override
061            public void addDocument(Document document) throws PortalException {
062                    if (_documents == null) {
063                            _documents = new ArrayList<>();
064                    }
065    
066                    _documents.add(document);
067    
068                    if (_documents.size() >= _interval) {
069                            indexInterval();
070                    }
071            }
072    
073            @Override
074            public AddCriteriaMethod getAddCriteriaMethod() {
075                    return _addCriteriaMethod;
076            }
077    
078            @Override
079            public AddOrderCriteriaMethod getAddOrderCriteriaMethod() {
080                    return _addOrderCriteriaMethod;
081            }
082    
083            @Override
084            public PerformActionMethod<?> getPerformActionMethod() {
085                    return _performActionMethod;
086            }
087    
088            @Override
089            public PerformCountMethod getPerformCountMethod() {
090                    return _performCountMethod;
091            }
092    
093            @Override
094            public void performActions() throws PortalException {
095                    long previousPrimaryKey = -1;
096    
097                    while (true) {
098                            long lastPrimaryKey = doPerformActions(previousPrimaryKey);
099    
100                            if (lastPrimaryKey < 0) {
101                                    return;
102                            }
103    
104                            intervalCompleted(previousPrimaryKey, lastPrimaryKey);
105    
106                            previousPrimaryKey = lastPrimaryKey;
107                    }
108            }
109    
110            @Override
111            public long performCount() throws PortalException {
112                    if (_performCountMethod != null) {
113                            return _performCountMethod.performCount();
114                    }
115    
116                    DynamicQuery dynamicQuery = DynamicQueryFactoryUtil.forClass(
117                            _clazz, _classLoader);
118    
119                    addDefaultCriteria(dynamicQuery);
120    
121                    addCriteria(dynamicQuery);
122    
123                    return (Long)executeDynamicQuery(
124                            _dynamicQueryCountMethod, dynamicQuery, getCountProjection());
125            }
126    
127            @Override
128            public void setAddCriteriaMethod(AddCriteriaMethod addCriteriaMethod) {
129                    _addCriteriaMethod = addCriteriaMethod;
130            }
131    
132            @Override
133            public void setAddOrderCriteriaMethod(
134                    AddOrderCriteriaMethod addOrderCriteriaMethod) {
135    
136                    _addOrderCriteriaMethod = addOrderCriteriaMethod;
137            }
138    
139            @Override
140            public void setBaseLocalService(BaseLocalService baseLocalService) {
141                    _baseLocalService = baseLocalService;
142    
143                    Class<?> clazz = _baseLocalService.getClass();
144    
145                    try {
146                            _dynamicQueryMethod = clazz.getMethod(
147                                    "dynamicQuery", DynamicQuery.class);
148                            _dynamicQueryCountMethod = clazz.getMethod(
149                                    "dynamicQueryCount", DynamicQuery.class, Projection.class);
150                    }
151                    catch (NoSuchMethodException nsme) {
152                            throw new SystemException(nsme);
153                    }
154            }
155    
156            @Override
157            public void setClass(Class<?> clazz) {
158                    _clazz = clazz;
159            }
160    
161            @Override
162            public void setClassLoader(ClassLoader classLoader) {
163                    _classLoader = classLoader;
164            }
165    
166            @Override
167            public void setCommitImmediately(boolean commitImmediately) {
168                    _commitImmediately = commitImmediately;
169            }
170    
171            @Override
172            public void setCompanyId(long companyId) {
173                    _companyId = companyId;
174            }
175    
176            @Override
177            public void setGroupId(long groupId) {
178                    _groupId = groupId;
179            }
180    
181            @Override
182            public void setGroupIdPropertyName(String groupIdPropertyName) {
183                    _groupIdPropertyName = groupIdPropertyName;
184            }
185    
186            @Override
187            public void setInterval(int interval) {
188                    _interval = interval;
189            }
190    
191            @Override
192            public void setParallel(boolean parallel) {
193                    _parallel = parallel;
194            }
195    
196            @Override
197            public void setPerformActionMethod(
198                    PerformActionMethod<?> performActionMethod) {
199    
200                    _performActionMethod = performActionMethod;
201            }
202    
203            @Override
204            public void setPerformCountMethod(PerformCountMethod performCountMethod) {
205                    _performCountMethod = performCountMethod;
206            }
207    
208            @Override
209            public void setPrimaryKeyPropertyName(String primaryKeyPropertyName) {
210                    _primaryKeyPropertyName = primaryKeyPropertyName;
211            }
212    
213            @Override
214            public void setSearchEngineId(String searchEngineId) {
215                    _searchEngineId = searchEngineId;
216            }
217    
218            @Override
219            public void setTransactionAttribute(
220                    TransactionAttribute transactionAttribute) {
221    
222                    _transactionAttribute = transactionAttribute;
223            }
224    
225            protected void addCriteria(DynamicQuery dynamicQuery) {
226                    if (_addCriteriaMethod != null) {
227                            _addCriteriaMethod.addCriteria(dynamicQuery);
228                    }
229            }
230    
231            protected void addDefaultCriteria(DynamicQuery dynamicQuery) {
232                    if (_companyId > 0) {
233                            Property property = PropertyFactoryUtil.forName("companyId");
234    
235                            dynamicQuery.add(property.eq(_companyId));
236                    }
237    
238                    if (_groupId > 0) {
239                            Property property = PropertyFactoryUtil.forName(
240                                    _groupIdPropertyName);
241    
242                            dynamicQuery.add(property.eq(_groupId));
243                    }
244            }
245    
246            protected void addDocuments(Collection<Document> documents)
247                    throws PortalException {
248    
249                    if (_documents == null) {
250                            _documents = new ArrayList<>();
251                    }
252    
253                    _documents.addAll(documents);
254    
255                    if (_documents.size() >= _interval) {
256                            indexInterval();
257                    }
258            }
259    
260            protected void addOrderCriteria(DynamicQuery dynamicQuery) {
261                    if (_addOrderCriteriaMethod != null) {
262                            _addOrderCriteriaMethod.addOrderCriteria(dynamicQuery);
263                    }
264                    else {
265                            dynamicQuery.addOrder(
266                                    OrderFactoryUtil.asc(_primaryKeyPropertyName));
267                    }
268            }
269    
270            protected long doPerformActions(long previousPrimaryKey)
271                    throws PortalException {
272    
273                    final DynamicQuery dynamicQuery = DynamicQueryFactoryUtil.forClass(
274                            _clazz, _classLoader);
275    
276                    Property property = PropertyFactoryUtil.forName(
277                            _primaryKeyPropertyName);
278    
279                    dynamicQuery.add(property.gt(previousPrimaryKey));
280    
281                    dynamicQuery.setLimit(0, _interval);
282    
283                    addDefaultCriteria(dynamicQuery);
284    
285                    addCriteria(dynamicQuery);
286    
287                    addOrderCriteria(dynamicQuery);
288    
289                    Callable<Long> callable = new Callable<Long>() {
290    
291                            @Override
292                            public Long call() throws Exception {
293                                    List<Object> objects = (List<Object>)executeDynamicQuery(
294                                            _dynamicQueryMethod, dynamicQuery);
295    
296                                    if (objects.isEmpty()) {
297                                            return -1L;
298                                    }
299    
300                                    if (_parallel) {
301                                            List<Future<Void>> futures = new ArrayList<>(
302                                                    objects.size());
303    
304                                            for (final Object object : objects) {
305                                                    futures.add(
306                                                            _threadPoolExecutor.submit(
307                                                                    new Callable<Void>() {
308    
309                                                                            @Override
310                                                                            public Void call() throws PortalException {
311                                                                                    performAction(object);
312    
313                                                                                    return null;
314                                                                            }
315    
316                                                                    }));
317                                            }
318    
319                                            for (Future<Void> future : futures) {
320                                                    future.get();
321                                            }
322                                    }
323                                    else {
324                                            for (Object object : objects) {
325                                                    performAction(object);
326                                            }
327                                    }
328    
329                                    if (objects.size() < _interval) {
330                                            return -1L;
331                                    }
332    
333                                    BaseModel<?> baseModel = (BaseModel<?>)objects.get(
334                                            objects.size() - 1);
335    
336                                    return (Long)baseModel.getPrimaryKeyObj();
337                            }
338    
339                    };
340    
341                    TransactionAttribute transactionAttribute = getTransactionAttribute();
342    
343                    try {
344                            if (transactionAttribute == null) {
345                                    return callable.call();
346                            }
347                            else {
348                                    return TransactionInvokerUtil.invoke(
349                                            transactionAttribute, callable);
350                            }
351                    }
352                    catch (Throwable t) {
353                            if (t instanceof PortalException) {
354                                    throw (PortalException)t;
355                            }
356    
357                            if (t instanceof SystemException) {
358                                    throw (SystemException)t;
359                            }
360    
361                            throw new SystemException(t);
362                    }
363                    finally {
364                            indexInterval();
365                    }
366            }
367    
368            protected Object executeDynamicQuery(
369                            Method dynamicQueryMethod, Object... arguments)
370                    throws PortalException {
371    
372                    try {
373                            return dynamicQueryMethod.invoke(_baseLocalService, arguments);
374                    }
375                    catch (InvocationTargetException ite) {
376                            Throwable throwable = ite.getCause();
377    
378                            if (throwable instanceof PortalException) {
379                                    throw (PortalException)throwable;
380                            }
381                            else if (throwable instanceof SystemException) {
382                                    throw (SystemException)throwable;
383                            }
384    
385                            throw new SystemException(ite);
386                    }
387                    catch (Exception e) {
388                            throw new SystemException(e);
389                    }
390            }
391    
392            protected Projection getCountProjection() {
393                    return ProjectionFactoryUtil.rowCount();
394            }
395    
396            protected String getSearchEngineId() {
397                    return _searchEngineId;
398            }
399    
400            protected TransactionAttribute getTransactionAttribute() {
401                    return _transactionAttribute;
402            }
403    
404            protected void indexInterval() throws PortalException {
405                    if ((_documents == null) || _documents.isEmpty()) {
406                            return;
407                    }
408    
409                    if (Validator.isNull(_searchEngineId)) {
410                            _searchEngineId = SearchEngineUtil.getSearchEngineId(_documents);
411                    }
412    
413                    SearchEngineUtil.updateDocuments(
414                            _searchEngineId, _companyId, new ArrayList<Document>(_documents),
415                            _commitImmediately);
416    
417                    _documents.clear();
418            }
419    
420            @SuppressWarnings("unused")
421            protected void intervalCompleted(long startPrimaryKey, long endPrimaryKey)
422                    throws PortalException {
423            }
424    
425            protected void performAction(Object object) throws PortalException {
426                    if (_performActionMethod != null) {
427                            _performActionMethod.performAction(object);
428                    }
429            }
430    
431            private AddCriteriaMethod _addCriteriaMethod;
432            private AddOrderCriteriaMethod _addOrderCriteriaMethod;
433            private BaseLocalService _baseLocalService;
434            private ClassLoader _classLoader;
435            private Class<?> _clazz;
436            private boolean _commitImmediately;
437            private long _companyId;
438            private Collection<Document> _documents;
439            private Method _dynamicQueryCountMethod;
440            private Method _dynamicQueryMethod;
441            private long _groupId;
442            private String _groupIdPropertyName = "groupId";
443            private int _interval = Indexer.DEFAULT_INTERVAL;
444            private boolean _parallel;
445    
446            @SuppressWarnings("rawtypes")
447            private PerformActionMethod _performActionMethod;
448    
449            private PerformCountMethod _performCountMethod;
450            private String _primaryKeyPropertyName;
451            private String _searchEngineId;
452            private final ThreadPoolExecutor _threadPoolExecutor =
453                    PortalExecutorManagerUtil.getPortalExecutor(
454                            BaseActionableDynamicQuery.class.getName());
455            private TransactionAttribute _transactionAttribute;
456    
457    }