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.portlet.exportimport.backgroundtask;
016    
017    import static com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleConstants.EVENT_PUBLICATION_LAYOUT_REMOTE_FAILED;
018    import static com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleConstants.EVENT_PUBLICATION_LAYOUT_REMOTE_STARTED;
019    import static com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleConstants.EVENT_PUBLICATION_LAYOUT_REMOTE_SUCCEEDED;
020    import static com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleConstants.PROCESS_FLAG_LAYOUT_STAGING_IN_PROCESS;
021    
022    import com.liferay.portal.NoSuchLayoutException;
023    import com.liferay.portal.kernel.backgroundtask.BackgroundTaskResult;
024    import com.liferay.portal.kernel.exception.PortalException;
025    import com.liferay.portal.kernel.exception.SystemException;
026    import com.liferay.portal.kernel.log.Log;
027    import com.liferay.portal.kernel.log.LogFactoryUtil;
028    import com.liferay.portal.kernel.util.FileUtil;
029    import com.liferay.portal.kernel.util.GetterUtil;
030    import com.liferay.portal.kernel.util.MapUtil;
031    import com.liferay.portal.kernel.util.StreamUtil;
032    import com.liferay.portal.model.BackgroundTask;
033    import com.liferay.portal.model.Layout;
034    import com.liferay.portal.security.auth.HttpPrincipal;
035    import com.liferay.portal.service.LayoutLocalServiceUtil;
036    import com.liferay.portal.service.http.LayoutServiceHttp;
037    import com.liferay.portal.util.PropsValues;
038    import com.liferay.portlet.exportimport.lar.ExportImportHelperUtil;
039    import com.liferay.portlet.exportimport.lar.ExportImportThreadLocal;
040    import com.liferay.portlet.exportimport.lar.MissingReferences;
041    import com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleManager;
042    import com.liferay.portlet.exportimport.model.ExportImportConfiguration;
043    import com.liferay.portlet.exportimport.service.ExportImportLocalServiceUtil;
044    import com.liferay.portlet.exportimport.service.http.StagingServiceHttp;
045    
046    import java.io.File;
047    import java.io.FileInputStream;
048    import java.io.Serializable;
049    
050    import java.util.ArrayList;
051    import java.util.List;
052    import java.util.Map;
053    
054    /**
055     * @author Mate Thurzo
056     */
057    public class LayoutRemoteStagingBackgroundTaskExecutor
058            extends BaseStagingBackgroundTaskExecutor {
059    
060            public LayoutRemoteStagingBackgroundTaskExecutor() {
061                    setBackgroundTaskStatusMessageTranslator(
062                            new LayoutStagingBackgroundTaskStatusMessageTranslator());
063            }
064    
065            @Override
066            public BackgroundTaskResult execute(BackgroundTask backgroundTask) {
067                    ExportImportConfiguration exportImportConfiguration =
068                            getExportImportConfiguration(backgroundTask);
069    
070                    clearBackgroundTaskStatus(backgroundTask);
071    
072                    File file = null;
073                    HttpPrincipal httpPrincipal = null;
074                    MissingReferences missingReferences = null;
075                    long stagingRequestId = 0L;
076    
077                    try {
078                            ExportImportThreadLocal.setLayoutStagingInProcess(true);
079    
080                            ExportImportLifecycleManager.fireExportImportLifecycleEvent(
081                                    EVENT_PUBLICATION_LAYOUT_REMOTE_STARTED,
082                                    PROCESS_FLAG_LAYOUT_STAGING_IN_PROCESS,
083                                    exportImportConfiguration);
084    
085                            Map<String, Serializable> settingsMap =
086                                    exportImportConfiguration.getSettingsMap();
087    
088                            long sourceGroupId = MapUtil.getLong(settingsMap, "sourceGroupId");
089                            boolean privateLayout = MapUtil.getBoolean(
090                                    settingsMap, "privateLayout");
091    
092                            initThreadLocals(sourceGroupId, privateLayout);
093    
094                            Map<Long, Boolean> layoutIdMap =
095                                    (Map<Long, Boolean>)settingsMap.get("layoutIdMap");
096                            long remoteGroupId = MapUtil.getLong(settingsMap, "remoteGroupId");
097    
098                            Map<String, Serializable> taskContextMap =
099                                    backgroundTask.getTaskContextMap();
100    
101                            httpPrincipal = (HttpPrincipal)taskContextMap.get("httpPrincipal");
102    
103                            file = exportLayoutsAsFile(
104                                    exportImportConfiguration, layoutIdMap, remoteGroupId,
105                                    httpPrincipal);
106    
107                            String checksum = FileUtil.getMD5Checksum(file);
108    
109                            stagingRequestId = StagingServiceHttp.createStagingRequest(
110                                    httpPrincipal, remoteGroupId, checksum);
111    
112                            transferFileToRemoteLive(file, stagingRequestId, httpPrincipal);
113    
114                            markBackgroundTask(
115                                    backgroundTask.getBackgroundTaskId(), "exported");
116    
117                            missingReferences = StagingServiceHttp.publishStagingRequest(
118                                    httpPrincipal, stagingRequestId, exportImportConfiguration);
119    
120                            ExportImportThreadLocal.setLayoutStagingInProcess(false);
121    
122                            ExportImportLifecycleManager.fireExportImportLifecycleEvent(
123                                    EVENT_PUBLICATION_LAYOUT_REMOTE_SUCCEEDED,
124                                    PROCESS_FLAG_LAYOUT_STAGING_IN_PROCESS,
125                                    exportImportConfiguration);
126                    }
127                    catch (Throwable t) {
128                            ExportImportThreadLocal.setLayoutStagingInProcess(false);
129    
130                            ExportImportLifecycleManager.fireExportImportLifecycleEvent(
131                                    EVENT_PUBLICATION_LAYOUT_REMOTE_FAILED,
132                                    PROCESS_FLAG_LAYOUT_STAGING_IN_PROCESS,
133                                    exportImportConfiguration);
134    
135                            if (_log.isDebugEnabled()) {
136                                    _log.debug(t, t);
137                            }
138                            else if (_log.isWarnEnabled()) {
139                                    _log.warn("Unable to publish layout: " + t.getMessage());
140                            }
141    
142                            deleteTempLarOnFailure(file);
143    
144                            throw new SystemException(t);
145                    }
146                    finally {
147                            if ((stagingRequestId > 0) && (httpPrincipal != null)) {
148                                    try {
149                                            StagingServiceHttp.cleanUpStagingRequest(
150                                                    httpPrincipal, stagingRequestId);
151                                    }
152                                    catch (PortalException pe) {
153                                            _log.warn("Unable to clean up the remote live site");
154                                    }
155                            }
156                    }
157    
158                    deleteTempLarOnSuccess(file);
159    
160                    return processMissingReferences(
161                            backgroundTask.getBackgroundTaskId(), missingReferences);
162            }
163    
164            protected File exportLayoutsAsFile(
165                            ExportImportConfiguration exportImportConfiguration,
166                            Map<Long, Boolean> layoutIdMap, long remoteGroupId,
167                            HttpPrincipal httpPrincipal)
168                    throws PortalException {
169    
170                    List<Layout> layouts = new ArrayList<>();
171    
172                    if (layoutIdMap != null) {
173                            for (Map.Entry<Long, Boolean> entry : layoutIdMap.entrySet()) {
174                                    long plid = GetterUtil.getLong(String.valueOf(entry.getKey()));
175                                    boolean includeChildren = entry.getValue();
176    
177                                    Layout layout = LayoutLocalServiceUtil.getLayout(plid);
178    
179                                    if (!layouts.contains(layout)) {
180                                            layouts.add(layout);
181                                    }
182    
183                                    List<Layout> parentLayouts = getMissingRemoteParentLayouts(
184                                            httpPrincipal, layout, remoteGroupId);
185    
186                                    for (Layout parentLayout : parentLayouts) {
187                                            if (!layouts.contains(parentLayout)) {
188                                                    layouts.add(parentLayout);
189                                            }
190                                    }
191    
192                                    if (includeChildren) {
193                                            for (Layout childLayout : layout.getAllChildren()) {
194                                                    if (!layouts.contains(childLayout)) {
195                                                            layouts.add(childLayout);
196                                                    }
197                                            }
198                                    }
199                            }
200                    }
201    
202                    long[] layoutIds = ExportImportHelperUtil.getLayoutIds(layouts);
203    
204                    Map<String, Serializable> settingsMap =
205                            exportImportConfiguration.getSettingsMap();
206    
207                    settingsMap.remove("layoutIdMap");
208    
209                    settingsMap.put("layoutIds", layoutIds);
210    
211                    return ExportImportLocalServiceUtil.exportLayoutsAsFile(
212                            exportImportConfiguration);
213            }
214    
215            /**
216             * @see com.liferay.portal.lar.ExportImportHelperImpl#getMissingParentLayouts(
217             *      Layout, long)
218             */
219            protected List<Layout> getMissingRemoteParentLayouts(
220                            HttpPrincipal httpPrincipal, Layout layout, long remoteGroupId)
221                    throws PortalException {
222    
223                    List<Layout> missingRemoteParentLayouts = new ArrayList<>();
224    
225                    long parentLayoutId = layout.getParentLayoutId();
226    
227                    while (parentLayoutId > 0) {
228                            Layout parentLayout = LayoutLocalServiceUtil.getLayout(
229                                    layout.getGroupId(), layout.isPrivateLayout(), parentLayoutId);
230    
231                            try {
232                                    LayoutServiceHttp.getLayoutByUuidAndGroupId(
233                                            httpPrincipal, parentLayout.getUuid(), remoteGroupId,
234                                            parentLayout.getPrivateLayout());
235    
236                                    // If one parent is found, all others are assumed to exist
237    
238                                    break;
239                            }
240                            catch (NoSuchLayoutException nsle) {
241                                    missingRemoteParentLayouts.add(parentLayout);
242    
243                                    parentLayoutId = parentLayout.getParentLayoutId();
244                            }
245                    }
246    
247                    return missingRemoteParentLayouts;
248            }
249    
250            protected void transferFileToRemoteLive(
251                            File file, long stagingRequestId, HttpPrincipal httpPrincipal)
252                    throws Exception {
253    
254                    byte[] bytes =
255                            new byte[PropsValues.STAGING_REMOTE_TRANSFER_BUFFER_SIZE];
256    
257                    int i = 0;
258                    int j = 0;
259    
260                    String numberFormat = String.format(
261                            "%%0%dd",
262                            String.valueOf((int) (file.length() / bytes.length)).length() + 1);
263    
264                    FileInputStream fileInputStream = null;
265    
266                    try {
267                            fileInputStream = new FileInputStream(file);
268    
269                            while ((i = fileInputStream.read(bytes)) >= 0) {
270                                    String fileName =
271                                            file.getName() + String.format(numberFormat, j++);
272    
273                                    if (i < PropsValues.STAGING_REMOTE_TRANSFER_BUFFER_SIZE) {
274                                            byte[] tempBytes = new byte[i];
275    
276                                            System.arraycopy(bytes, 0, tempBytes, 0, i);
277    
278                                            StagingServiceHttp.updateStagingRequest(
279                                                    httpPrincipal, stagingRequestId, fileName, tempBytes);
280                                    }
281                                    else {
282                                            StagingServiceHttp.updateStagingRequest(
283                                                    httpPrincipal, stagingRequestId, fileName, bytes);
284                                    }
285    
286                                    bytes =
287                                            new byte[PropsValues.STAGING_REMOTE_TRANSFER_BUFFER_SIZE];
288                            }
289                    }
290                    finally {
291                            StreamUtil.cleanUp(fileInputStream);
292                    }
293            }
294    
295            private static final Log _log = LogFactoryUtil.getLog(
296                    LayoutRemoteStagingBackgroundTaskExecutor.class);
297    
298    }