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.workflow;
016    
017    import aQute.bnd.annotation.ProviderType;
018    
019    import com.liferay.portal.kernel.exception.PortalException;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.transaction.TransactionCommitCallbackRegistryUtil;
023    import com.liferay.portal.kernel.util.GetterUtil;
024    import com.liferay.portal.kernel.util.ListUtil;
025    import com.liferay.portal.kernel.util.LocaleUtil;
026    import com.liferay.portal.model.WorkflowDefinitionLink;
027    import com.liferay.portal.model.WorkflowInstanceLink;
028    import com.liferay.portal.service.ServiceContext;
029    import com.liferay.portal.service.WorkflowInstanceLinkLocalServiceUtil;
030    import com.liferay.registry.Registry;
031    import com.liferay.registry.RegistryUtil;
032    import com.liferay.registry.ServiceReference;
033    import com.liferay.registry.ServiceRegistration;
034    import com.liferay.registry.ServiceTracker;
035    import com.liferay.registry.ServiceTrackerCustomizer;
036    import com.liferay.registry.collections.ServiceRegistrationMap;
037    
038    import java.io.Serializable;
039    
040    import java.util.Collections;
041    import java.util.HashMap;
042    import java.util.List;
043    import java.util.Map;
044    import java.util.TreeMap;
045    import java.util.concurrent.Callable;
046    import java.util.concurrent.ConcurrentSkipListMap;
047    
048    /**
049     * @author Bruno Farache
050     * @author Marcellus Tavares
051     */
052    @ProviderType
053    public class WorkflowHandlerRegistryUtil {
054    
055            public static List<WorkflowHandler<?>> getScopeableWorkflowHandlers() {
056                    return _instance._getScopeableWorkflowHandlers();
057            }
058    
059            public static <T> WorkflowHandler<T> getWorkflowHandler(String className) {
060                    return (WorkflowHandler<T>)_instance._getWorkflowHandler(className);
061            }
062    
063            public static List<WorkflowHandler<?>> getWorkflowHandlers() {
064                    return _instance._getWorkflowHandlers();
065            }
066    
067            public static void register(List<WorkflowHandler<?>> workflowHandlers) {
068                    for (WorkflowHandler<?> workflowHandler : workflowHandlers) {
069                            register(workflowHandler);
070                    }
071            }
072    
073            public static void register(WorkflowHandler<?> workflowHandler) {
074                    _instance._register(workflowHandler);
075            }
076    
077            public static <T> void startWorkflowInstance(
078                            long companyId, long groupId, long userId, String className,
079                            long classPK, T model, ServiceContext serviceContext)
080                    throws PortalException {
081    
082                    Map<String, Serializable> workflowContext =
083                            (Map<String, Serializable>)serviceContext.removeAttribute(
084                                    "workflowContext");
085    
086                    if (workflowContext == null) {
087                            workflowContext = Collections.emptyMap();
088                    }
089    
090                    startWorkflowInstance(
091                            companyId, groupId, userId, className, classPK, model,
092                            serviceContext, workflowContext);
093            }
094    
095            public static <T> T startWorkflowInstance(
096                            final long companyId, final long groupId, final long userId,
097                            String className, final long classPK, final T model,
098                            ServiceContext serviceContext,
099                            Map<String, Serializable> workflowContext)
100                    throws PortalException {
101    
102                    if (serviceContext.getWorkflowAction() !=
103                                    WorkflowConstants.ACTION_PUBLISH) {
104    
105                            return model;
106                    }
107    
108                    final WorkflowHandler<T> workflowHandler = getWorkflowHandler(
109                            className);
110    
111                    if (workflowHandler == null) {
112                            if (WorkflowThreadLocal.isEnabled()) {
113                                    throw new WorkflowException(
114                                            "No workflow handler found for " + className);
115                            }
116    
117                            return model;
118                    }
119    
120                    boolean hasWorkflowInstanceInProgress =
121                            _instance._hasWorkflowInstanceInProgress(
122                                    companyId, groupId, className, classPK);
123    
124                    if (hasWorkflowInstanceInProgress) {
125                            if (_log.isWarnEnabled()) {
126                                    _log.warn(
127                                            "Workflow already started for class " + className +
128                                                    " with primary key " + classPK + " in group " +
129                                                            groupId);
130                            }
131    
132                            return model;
133                    }
134    
135                    WorkflowDefinitionLink workflowDefinitionLink = null;
136    
137                    if (WorkflowThreadLocal.isEnabled() &&
138                            WorkflowEngineManagerUtil.isDeployed()) {
139    
140                            workflowDefinitionLink = workflowHandler.getWorkflowDefinitionLink(
141                                    companyId, groupId, classPK);
142                    }
143    
144                    int status = WorkflowConstants.STATUS_PENDING;
145    
146                    if (workflowDefinitionLink == null) {
147                            status = WorkflowConstants.STATUS_APPROVED;
148                    }
149    
150                    workflowContext = new HashMap<String, Serializable>(workflowContext);
151    
152                    workflowContext.put(
153                            WorkflowConstants.CONTEXT_COMPANY_ID, String.valueOf(companyId));
154                    workflowContext.put(
155                            WorkflowConstants.CONTEXT_GROUP_ID, String.valueOf(groupId));
156                    workflowContext.put(
157                            WorkflowConstants.CONTEXT_USER_ID, String.valueOf(userId));
158                    workflowContext.put(
159                            WorkflowConstants.CONTEXT_ENTRY_CLASS_NAME, className);
160                    workflowContext.put(
161                            WorkflowConstants.CONTEXT_ENTRY_CLASS_PK, String.valueOf(classPK));
162                    workflowContext.put(
163                            WorkflowConstants.CONTEXT_ENTRY_TYPE,
164                            workflowHandler.getType(LocaleUtil.getDefault()));
165                    workflowContext.put(
166                            WorkflowConstants.CONTEXT_SERVICE_CONTEXT, serviceContext);
167                    workflowContext.put(
168                            WorkflowConstants.CONTEXT_TASK_COMMENTS,
169                            GetterUtil.getString(serviceContext.getAttribute("comments")));
170    
171                    T updatedModel = workflowHandler.updateStatus(status, workflowContext);
172    
173                    if (workflowDefinitionLink != null) {
174                            final Map<String, Serializable> tempWorkflowContext =
175                                    workflowContext;
176    
177                            TransactionCommitCallbackRegistryUtil.registerCallback(
178                                    new Callable<Void>() {
179    
180                                            @Override
181                                            public Void call() throws Exception {
182                                                    workflowHandler.startWorkflowInstance(
183                                                            companyId, groupId, userId, classPK, model,
184                                                            tempWorkflowContext);
185    
186                                                    return null;
187                                            }
188    
189                                    });
190                    }
191    
192                    return updatedModel;
193            }
194    
195            public static <T> void startWorkflowInstance(
196                            long companyId, long userId, String className, long classPK,
197                            T model, ServiceContext serviceContext)
198                    throws PortalException {
199    
200                    Map<String, Serializable> workflowContext =
201                            (Map<String, Serializable>)serviceContext.removeAttribute(
202                                    "workflowContext");
203    
204                    if (workflowContext == null) {
205                            workflowContext = Collections.emptyMap();
206                    }
207    
208                    startWorkflowInstance(
209                            companyId, WorkflowConstants.DEFAULT_GROUP_ID, userId, className,
210                            classPK, model, serviceContext, workflowContext);
211            }
212    
213            public static <T> void startWorkflowInstance(
214                            long companyId, long userId, String className, long classPK,
215                            T model, ServiceContext serviceContext,
216                            Map<String, Serializable> workflowContext)
217                    throws PortalException {
218    
219                    startWorkflowInstance(
220                            companyId, WorkflowConstants.DEFAULT_GROUP_ID, userId, className,
221                            classPK, model, serviceContext, workflowContext);
222            }
223    
224            public static void unregister(List<WorkflowHandler<?>> workflowHandlers) {
225                    for (WorkflowHandler<?> workflowHandler : workflowHandlers) {
226                            unregister(workflowHandler);
227                    }
228            }
229    
230            public static void unregister(WorkflowHandler<?> workflowHandler) {
231                    _instance._unregister(workflowHandler);
232            }
233    
234            public static <T> T updateStatus(
235                            int status, Map<String, Serializable> workflowContext)
236                    throws PortalException {
237    
238                    String className = (String)workflowContext.get(
239                            WorkflowConstants.CONTEXT_ENTRY_CLASS_NAME);
240    
241                    WorkflowHandler<T> workflowHandler = getWorkflowHandler(className);
242    
243                    if (workflowHandler != null) {
244                            return workflowHandler.updateStatus(status, workflowContext);
245                    }
246    
247                    return null;
248            }
249    
250            private WorkflowHandlerRegistryUtil() {
251                    Registry registry = RegistryUtil.getRegistry();
252    
253                    _serviceTracker = registry.trackServices(
254                            (Class<WorkflowHandler<?>>)(Class<?>)WorkflowHandler.class,
255                            new WorkflowHandlerServiceTrackerCustomizer());
256    
257                    _serviceTracker.open();
258            }
259    
260            private List<WorkflowHandler<?>> _getScopeableWorkflowHandlers() {
261                    return ListUtil.fromMapValues(_scopeableWorkflowHandlerMap);
262            }
263    
264            private WorkflowHandler<?> _getWorkflowHandler(String className) {
265                    return _workflowHandlerMap.get(className);
266            }
267    
268            private List<WorkflowHandler<?>> _getWorkflowHandlers() {
269                    return ListUtil.fromMapValues(_workflowHandlerMap);
270            }
271    
272            private boolean _hasWorkflowInstanceInProgress(
273                            long companyId, long groupId, String className, long classPK)
274                    throws PortalException {
275    
276                    WorkflowInstanceLink workflowInstanceLink =
277                            WorkflowInstanceLinkLocalServiceUtil.fetchWorkflowInstanceLink(
278                                    companyId, groupId, className, classPK);
279    
280                    if (workflowInstanceLink == null) {
281                            return false;
282                    }
283    
284                    WorkflowInstance workflowInstance =
285                            WorkflowInstanceManagerUtil.getWorkflowInstance(
286                                    companyId, workflowInstanceLink.getWorkflowInstanceId());
287    
288                    if (!workflowInstance.isComplete()) {
289                            return true;
290                    }
291    
292                    return false;
293            }
294    
295            private void _register(WorkflowHandler<?> workflowHandler) {
296                    Registry registry = RegistryUtil.getRegistry();
297    
298                    ServiceRegistration<WorkflowHandler<?>> serviceRegistration =
299                            registry.registerService(
300                                    (Class<WorkflowHandler<?>>)(Class<?>)WorkflowHandler.class,
301                                    workflowHandler);
302    
303                    _serviceRegistrations.put(workflowHandler, serviceRegistration);
304            }
305    
306            private void _unregister(WorkflowHandler<?> workflowHandler) {
307                    ServiceRegistration<WorkflowHandler<?>> serviceRegistration =
308                            _serviceRegistrations.remove(workflowHandler);
309    
310                    if (serviceRegistration != null) {
311                            serviceRegistration.unregister();
312                    }
313            }
314    
315            private static final Log _log = LogFactoryUtil.getLog(
316                    WorkflowHandlerRegistryUtil.class);
317    
318            private static final WorkflowHandlerRegistryUtil _instance =
319                    new WorkflowHandlerRegistryUtil();
320    
321            private final Map<String, WorkflowHandler<?>> _scopeableWorkflowHandlerMap =
322                    new ConcurrentSkipListMap<String, WorkflowHandler<?>>();
323            private final ServiceRegistrationMap<WorkflowHandler<?>>
324                    _serviceRegistrations =
325                            new ServiceRegistrationMap<WorkflowHandler<?>>();
326            private final ServiceTracker<WorkflowHandler<?>, WorkflowHandler<?>>
327                    _serviceTracker;
328            private final Map<String, WorkflowHandler<?>> _workflowHandlerMap =
329                    new TreeMap<String, WorkflowHandler<?>>();
330    
331            private class WorkflowHandlerServiceTrackerCustomizer
332                    implements
333                            ServiceTrackerCustomizer <WorkflowHandler<?>, WorkflowHandler<?>> {
334    
335                    @Override
336                    public WorkflowHandler<?> addingService(
337                            ServiceReference<WorkflowHandler<?>> serviceReference) {
338    
339                            Registry registry = RegistryUtil.getRegistry();
340    
341                            WorkflowHandler<?> workflowHandler = registry.getService(
342                                    serviceReference);
343    
344                            _workflowHandlerMap.put(
345                                    workflowHandler.getClassName(), workflowHandler);
346    
347                            if (workflowHandler.isScopeable()) {
348                                    _scopeableWorkflowHandlerMap.put(
349                                            workflowHandler.getClassName(), workflowHandler);
350                            }
351    
352                            return workflowHandler;
353                    }
354    
355                    @Override
356                    public void modifiedService(
357                            ServiceReference<WorkflowHandler<?>> serviceReference,
358                            WorkflowHandler<?> workflowHandler) {
359                    }
360    
361                    @Override
362                    public void removedService(
363                            ServiceReference<WorkflowHandler<?>> serviceReference,
364                            WorkflowHandler<?> workflowHandler) {
365    
366                            Registry registry = RegistryUtil.getRegistry();
367    
368                            registry.ungetService(serviceReference);
369    
370                            _workflowHandlerMap.remove(workflowHandler.getClassName());
371    
372                            if (workflowHandler.isScopeable()) {
373                                    _scopeableWorkflowHandlerMap.remove(
374                                            workflowHandler.getClassName());
375                            }
376                    }
377    
378            }
379    
380    }