001    /**
002     * Copyright (c) 2000-2013 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.NoSuchLayoutException;
018    import com.liferay.portal.RemoteExportException;
019    import com.liferay.portal.kernel.backgroundtask.BackgroundTaskResult;
020    import com.liferay.portal.kernel.lar.ExportImportHelperUtil;
021    import com.liferay.portal.kernel.lar.ExportImportThreadLocal;
022    import com.liferay.portal.kernel.lar.MissingReferences;
023    import com.liferay.portal.kernel.lar.PortletDataHandlerKeys;
024    import com.liferay.portal.kernel.log.Log;
025    import com.liferay.portal.kernel.log.LogFactoryUtil;
026    import com.liferay.portal.kernel.staging.StagingUtil;
027    import com.liferay.portal.kernel.util.DateRange;
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.service.http.StagingServiceHttp;
038    import com.liferay.portal.util.PropsValues;
039    
040    import java.io.File;
041    import java.io.FileInputStream;
042    import java.io.Serializable;
043    
044    import java.util.ArrayList;
045    import java.util.Date;
046    import java.util.List;
047    import java.util.Map;
048    
049    /**
050     * @author Mate Thurzo
051     */
052    public class LayoutRemoteStagingBackgroundTaskExecutor
053            extends BaseStagingBackgroundTaskExecutor {
054    
055            public LayoutRemoteStagingBackgroundTaskExecutor() {
056                    setBackgroundTaskStatusMessageTranslator(
057                            new LayoutStagingBackgroundTaskStatusMessageTranslator());
058            }
059    
060            @Override
061            public BackgroundTaskResult execute(BackgroundTask backgroundTask)
062                    throws Exception {
063    
064                    Map<String, Serializable> taskContextMap =
065                            backgroundTask.getTaskContextMap();
066    
067                    long sourceGroupId = MapUtil.getLong(taskContextMap, "groupId");
068                    boolean privateLayout = MapUtil.getBoolean(
069                            taskContextMap, "privateLayout");
070                    Map<Long, Boolean> layoutIdMap = (Map<Long, Boolean>)taskContextMap.get(
071                            "layoutIdMap");
072                    Map<String, String[]> parameterMap =
073                            (Map<String, String[]>)taskContextMap.get("parameterMap");
074                    long remoteGroupId = MapUtil.getLong(taskContextMap, "remoteGroupId");
075                    Date startDate = (Date)taskContextMap.get("startDate");
076                    Date endDate = (Date)taskContextMap.get("endDate");
077                    HttpPrincipal httpPrincipal = (HttpPrincipal)taskContextMap.get(
078                            "httpPrincipal");
079    
080                    clearBackgroundTaskStatus(backgroundTask);
081    
082                    long stagingRequestId = 0;
083    
084                    File file = null;
085                    FileInputStream fileInputStream = null;
086                    MissingReferences missingReferences = null;
087    
088                    try {
089                            ExportImportThreadLocal.setLayoutStagingInProcess(true);
090    
091                            file = exportLayoutsAsFile(
092                                    sourceGroupId, privateLayout, layoutIdMap, parameterMap,
093                                    remoteGroupId, startDate, endDate, httpPrincipal);
094    
095                            String checksum = FileUtil.getMD5Checksum(file);
096    
097                            fileInputStream = new FileInputStream(file);
098    
099                            stagingRequestId = StagingServiceHttp.createStagingRequest(
100                                    httpPrincipal, remoteGroupId, checksum);
101    
102                            byte[] bytes =
103                                    new byte[PropsValues.STAGING_REMOTE_TRANSFER_BUFFER_SIZE];
104    
105                            int i = 0;
106                            int j = 0;
107    
108                            String numberFormat = String.format(
109                                    "%%0%dd",
110                                    String.valueOf(
111                                            (int)(file.length() / bytes.length)).length() + 1);
112    
113                            while ((i = fileInputStream.read(bytes)) >= 0) {
114                                    String fileName =
115                                            file.getName() + String.format(numberFormat, j++);
116    
117                                    if (i < PropsValues.STAGING_REMOTE_TRANSFER_BUFFER_SIZE) {
118                                            byte[] tempBytes = new byte[i];
119    
120                                            System.arraycopy(bytes, 0, tempBytes, 0, i);
121    
122                                            StagingServiceHttp.updateStagingRequest(
123                                                    httpPrincipal, stagingRequestId, fileName, tempBytes);
124                                    }
125                                    else {
126                                            StagingServiceHttp.updateStagingRequest(
127                                                    httpPrincipal, stagingRequestId, fileName, bytes);
128                                    }
129    
130                                    bytes =
131                                            new byte[PropsValues.STAGING_REMOTE_TRANSFER_BUFFER_SIZE];
132                            }
133    
134                            backgroundTask = markBackgroundTask(backgroundTask, "exported");
135    
136                            missingReferences = StagingServiceHttp.validateStagingRequest(
137                                    httpPrincipal, stagingRequestId, privateLayout, parameterMap);
138    
139                            backgroundTask = markBackgroundTask(backgroundTask, "validated");
140    
141                            StagingServiceHttp.publishStagingRequest(
142                                    httpPrincipal, stagingRequestId, privateLayout, parameterMap);
143    
144                            boolean updateLastPublishDate = MapUtil.getBoolean(
145                                    parameterMap, PortletDataHandlerKeys.UPDATE_LAST_PUBLISH_DATE);
146    
147                            if (updateLastPublishDate) {
148                                    DateRange dateRange = new DateRange(startDate, endDate);
149    
150                                    StagingUtil.updateLastPublishDate(
151                                            sourceGroupId, privateLayout, dateRange, endDate);
152                            }
153                    }
154                    catch (Exception e) {
155                            if (PropsValues.STAGING_DELETE_TEMP_LAR_ON_FAILURE) {
156                                    FileUtil.delete(file);
157                            }
158                            else if ((file != null) && _log.isErrorEnabled()) {
159                                    _log.error("Kept temporary LAR file " + file.getAbsolutePath());
160                            }
161    
162                            throw e;
163                    }
164                    finally {
165                            ExportImportThreadLocal.setLayoutStagingInProcess(false);
166    
167                            StreamUtil.cleanUp(fileInputStream);
168    
169                            if (stagingRequestId > 0) {
170                                    StagingServiceHttp.cleanUpStagingRequest(
171                                            httpPrincipal, stagingRequestId);
172                            }
173                    }
174    
175                    if (PropsValues.STAGING_DELETE_TEMP_LAR_ON_SUCCESS) {
176                            FileUtil.delete(file);
177                    }
178                    else if ((file != null) && _log.isDebugEnabled()) {
179                            _log.debug("Kept temporary LAR file " + file.getAbsolutePath());
180                    }
181    
182                    return processMissingReferences(backgroundTask, missingReferences);
183            }
184    
185            protected File exportLayoutsAsFile(
186                            long sourceGroupId, boolean privateLayout,
187                            Map<Long, Boolean> layoutIdMap, Map<String, String[]> parameterMap,
188                            long remoteGroupId, Date startDate, Date endDate,
189                            HttpPrincipal httpPrincipal)
190                    throws Exception {
191    
192                    if ((layoutIdMap == null) || layoutIdMap.isEmpty()) {
193                            return LayoutLocalServiceUtil.exportLayoutsAsFile(
194                                    sourceGroupId, privateLayout, null, parameterMap, startDate,
195                                    endDate);
196                    }
197                    else {
198                            List<Layout> layouts = new ArrayList<Layout>();
199    
200                            for (Map.Entry<Long, Boolean> entry : layoutIdMap.entrySet()) {
201                                    long plid = GetterUtil.getLong(String.valueOf(entry.getKey()));
202                                    boolean includeChildren = entry.getValue();
203    
204                                    Layout layout = LayoutLocalServiceUtil.getLayout(plid);
205    
206                                    if (!layouts.contains(layout)) {
207                                            layouts.add(layout);
208                                    }
209    
210                                    List<Layout> parentLayouts = getMissingRemoteParentLayouts(
211                                            httpPrincipal, layout, remoteGroupId);
212    
213                                    for (Layout parentLayout : parentLayouts) {
214                                            if (!layouts.contains(parentLayout)) {
215                                                    layouts.add(parentLayout);
216                                            }
217                                    }
218    
219                                    if (includeChildren) {
220                                            for (Layout childLayout : layout.getAllChildren()) {
221                                                    if (!layouts.contains(childLayout)) {
222                                                            layouts.add(childLayout);
223                                                    }
224                                            }
225                                    }
226                            }
227    
228                            long[] layoutIds = ExportImportHelperUtil.getLayoutIds(layouts);
229    
230                            if (layoutIds.length <= 0) {
231                                    throw new RemoteExportException(
232                                            RemoteExportException.NO_LAYOUTS);
233                            }
234    
235                            return LayoutLocalServiceUtil.exportLayoutsAsFile(
236                                    sourceGroupId, privateLayout, layoutIds, parameterMap,
237                                    startDate, endDate);
238                    }
239            }
240    
241            /**
242             * @see com.liferay.portal.staging.StagingImpl#getMissingParentLayouts(
243             *      Layout, long)
244             */
245            protected List<Layout> getMissingRemoteParentLayouts(
246                            HttpPrincipal httpPrincipal, Layout layout, long remoteGroupId)
247                    throws Exception {
248    
249                    List<Layout> missingRemoteParentLayouts = new ArrayList<Layout>();
250    
251                    long parentLayoutId = layout.getParentLayoutId();
252    
253                    while (parentLayoutId > 0) {
254                            Layout parentLayout = LayoutLocalServiceUtil.getLayout(
255                                    layout.getGroupId(), layout.isPrivateLayout(), parentLayoutId);
256    
257                            try {
258                                    LayoutServiceHttp.getLayoutByUuidAndGroupId(
259                                            httpPrincipal, parentLayout.getUuid(), remoteGroupId,
260                                            parentLayout.getPrivateLayout());
261    
262                                    // If one parent is found all others are assumed to exist
263    
264                                    break;
265                            }
266                            catch (NoSuchLayoutException nsle) {
267                                    missingRemoteParentLayouts.add(parentLayout);
268    
269                                    parentLayoutId = parentLayout.getParentLayoutId();
270                            }
271                    }
272    
273                    return missingRemoteParentLayouts;
274            }
275    
276            private static Log _log = LogFactoryUtil.getLog(
277                    LayoutRemoteStagingBackgroundTaskExecutor.class);
278    }