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.convert;
016    
017    import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
018    import com.liferay.portal.kernel.dao.orm.DynamicQuery;
019    import com.liferay.portal.kernel.dao.orm.Property;
020    import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
021    import com.liferay.portal.kernel.exception.PortalException;
022    import com.liferay.portal.kernel.exception.SystemException;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.repository.model.FileEntry;
026    import com.liferay.portal.kernel.util.GetterUtil;
027    import com.liferay.portal.kernel.util.InstanceFactory;
028    import com.liferay.portal.kernel.util.ListUtil;
029    import com.liferay.portal.kernel.util.PropsKeys;
030    import com.liferay.portal.kernel.util.StringBundler;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.kernel.util.Validator;
033    import com.liferay.portal.kernel.workflow.WorkflowConstants;
034    import com.liferay.portal.model.Image;
035    import com.liferay.portal.service.ImageLocalServiceUtil;
036    import com.liferay.portal.util.ClassLoaderUtil;
037    import com.liferay.portal.util.MaintenanceUtil;
038    import com.liferay.portal.util.PropsValues;
039    import com.liferay.portlet.documentlibrary.model.DLFileEntry;
040    import com.liferay.portlet.documentlibrary.model.DLFileVersion;
041    import com.liferay.portlet.documentlibrary.model.DLFolderConstants;
042    import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
043    import com.liferay.portlet.documentlibrary.store.AdvancedFileSystemStore;
044    import com.liferay.portlet.documentlibrary.store.CMISStore;
045    import com.liferay.portlet.documentlibrary.store.DBStore;
046    import com.liferay.portlet.documentlibrary.store.FileSystemStore;
047    import com.liferay.portlet.documentlibrary.store.JCRStore;
048    import com.liferay.portlet.documentlibrary.store.S3Store;
049    import com.liferay.portlet.documentlibrary.store.Store;
050    import com.liferay.portlet.documentlibrary.store.StoreFactory;
051    import com.liferay.portlet.documentlibrary.util.DLPreviewableProcessor;
052    import com.liferay.portlet.documentlibrary.util.comparator.FileVersionVersionComparator;
053    import com.liferay.portlet.messageboards.model.MBMessage;
054    import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
055    import com.liferay.registry.Registry;
056    import com.liferay.registry.RegistryUtil;
057    
058    import java.io.InputStream;
059    
060    import java.util.Collection;
061    import java.util.List;
062    
063    /**
064     * @author Minhchau Dang
065     * @author Alexander Chow
066     * @author L??szl?? Csontos
067     */
068    public class ConvertDocumentLibrary
069            extends BaseConvertProcess implements DLStoreConverter {
070    
071            @Override
072            public String getDescription() {
073                    return "migrate-documents-from-one-repository-to-another";
074            }
075    
076            @Override
077            public String getParameterDescription() {
078                    return "please-select-a-new-repository-hook";
079            }
080    
081            @Override
082            public String[] getParameterNames() {
083                    StringBundler sb = new StringBundler(_HOOKS.length * 2 + 2);
084    
085                    sb.append(PropsKeys.DL_STORE_IMPL);
086                    sb.append(StringPool.EQUAL);
087    
088                    for (String hook : _HOOKS) {
089                            if (!hook.equals(PropsValues.DL_STORE_IMPL)) {
090                                    sb.append(hook);
091                                    sb.append(StringPool.SEMICOLON);
092                            }
093                    }
094    
095                    return new String[] {
096                            sb.toString(), "delete-files-from-previous-repository=checkbox"
097                    };
098            }
099    
100            @Override
101            public boolean isEnabled() {
102                    return true;
103            }
104    
105            @Override
106            public void migrateDLFileEntry(
107                    long companyId, long repositoryId, DLFileEntry dlFileEntry) {
108    
109                    String fileName = dlFileEntry.getName();
110    
111                    List<DLFileVersion> dlFileVersions = getDLFileVersions(dlFileEntry);
112    
113                    if (dlFileVersions.isEmpty()) {
114                            String versionNumber = Store.VERSION_DEFAULT;
115    
116                            migrateFile(companyId, repositoryId, fileName, versionNumber);
117    
118                            return;
119                    }
120    
121                    for (DLFileVersion dlFileVersion : dlFileVersions) {
122                            String versionNumber = dlFileVersion.getVersion();
123    
124                            migrateFile(companyId, repositoryId, fileName, versionNumber);
125                    }
126            }
127    
128            @Override
129            public void validate() throws FileSystemStoreRootDirException {
130                    String sourceStoreClassName = getSourceStoreClassName();
131    
132                    if (!sourceStoreClassName.endsWith(_FILE_SYSTEM_STORE_SUFFIX)) {
133                            return;
134                    }
135    
136                    String targetStoreClassName = getTargetStoreClassName();
137    
138                    if (!targetStoreClassName.endsWith(_FILE_SYSTEM_STORE_SUFFIX)) {
139                            return;
140                    }
141    
142                    if (Validator.isBlank(
143                                    PropsValues.DL_STORE_ADVANCED_FILE_SYSTEM_ROOT_DIR)) {
144    
145                            if (_log.isWarnEnabled()) {
146                                    _log.warn(
147                                            "Property \"" +
148                                                    PropsKeys.DL_STORE_ADVANCED_FILE_SYSTEM_ROOT_DIR +
149                                                            " is not set");
150                            }
151    
152                            throw new FileSystemStoreRootDirException();
153                    }
154    
155                    if (Validator.isBlank(PropsValues.DL_STORE_FILE_SYSTEM_ROOT_DIR)) {
156                            if (_log.isWarnEnabled()) {
157                                    _log.warn(
158                                            "Property \"" +
159                                                    PropsKeys.DL_STORE_FILE_SYSTEM_ROOT_DIR +
160                                                            " is not set");
161                            }
162    
163                            throw new FileSystemStoreRootDirException();
164                    }
165    
166                    if (PropsValues.DL_STORE_ADVANCED_FILE_SYSTEM_ROOT_DIR.equals(
167                                    PropsValues.DL_STORE_FILE_SYSTEM_ROOT_DIR)) {
168    
169                            if (_log.isWarnEnabled()) {
170                                    StringBundler sb = new StringBundler(5);
171    
172                                    sb.append("Both properties \"");
173                                    sb.append(PropsKeys.DL_STORE_ADVANCED_FILE_SYSTEM_ROOT_DIR);
174                                    sb.append("\" and \"");
175                                    sb.append(PropsKeys.DL_STORE_FILE_SYSTEM_ROOT_DIR);
176                                    sb.append("\" have the same value");
177    
178                                    _log.warn(sb.toString());
179                            }
180    
181                            throw new FileSystemStoreRootDirException();
182                    }
183            }
184    
185            @Override
186            protected void doConvert() throws Exception {
187                    _sourceStore = StoreFactory.getInstance();
188    
189                    String targetStoreClassName = getTargetStoreClassName();
190    
191                    _targetStore = (Store)InstanceFactory.newInstance(
192                            ClassLoaderUtil.getPortalClassLoader(), targetStoreClassName);
193    
194                    migratePortlets();
195    
196                    StoreFactory.setInstance(_targetStore);
197    
198                    MaintenanceUtil.appendStatus(
199                            "Please set " + PropsKeys.DL_STORE_IMPL +
200                                    " in your portal-ext.properties to use " +
201                                            targetStoreClassName);
202    
203                    PropsValues.DL_STORE_IMPL = targetStoreClassName;
204            }
205    
206            protected List<DLFileVersion> getDLFileVersions(DLFileEntry dlFileEntry) {
207                    List<DLFileVersion> dlFileVersions = dlFileEntry.getFileVersions(
208                            WorkflowConstants.STATUS_ANY);
209    
210                    return ListUtil.sort(
211                            dlFileVersions, new FileVersionVersionComparator(true));
212            }
213    
214            protected String getSourceStoreClassName() {
215                    Store sourceStore = StoreFactory.getInstance();
216    
217                    return sourceStore.getClass().getName();
218            }
219    
220            protected String getTargetStoreClassName() {
221                    String[] values = getParameterValues();
222    
223                    return values[0];
224            }
225    
226            protected boolean isDeleteFilesFromSourceStore() {
227                    String[] values = getParameterValues();
228    
229                    return GetterUtil.getBoolean(values[1]);
230            }
231    
232            protected void migrateDL() throws PortalException {
233                    int count = DLFileEntryLocalServiceUtil.getFileEntriesCount();
234    
235                    MaintenanceUtil.appendStatus(
236                            "Migrating " + count + " documents and media files");
237    
238                    ActionableDynamicQuery actionableDynamicQuery =
239                            DLFileEntryLocalServiceUtil.getActionableDynamicQuery();
240    
241                    actionableDynamicQuery.setAddCriteriaMethod(
242                            new ActionableDynamicQuery.AddCriteriaMethod() {
243    
244                                    @Override
245                                    public void addCriteria(DynamicQuery dynamicQuery) {
246                                            Property classNameIdProperty = PropertyFactoryUtil.forName(
247                                                    "classNameId");
248    
249                                            dynamicQuery.add(classNameIdProperty.eq(0L));
250                                    }
251    
252                            });
253                    actionableDynamicQuery.setPerformActionMethod(
254                            new ActionableDynamicQuery.PerformActionMethod() {
255    
256                                    @Override
257                                    public void performAction(Object object) {
258                                            DLFileEntry dlFileEntry = (DLFileEntry)object;
259    
260                                            migrateDLFileEntry(
261                                                    dlFileEntry.getCompanyId(),
262                                                    dlFileEntry.getDataRepositoryId(), dlFileEntry);
263                                    }
264    
265                            });
266    
267                    actionableDynamicQuery.performActions();
268    
269                    if (isDeleteFilesFromSourceStore()) {
270                            DLPreviewableProcessor.deleteFiles();
271                    }
272            }
273    
274            protected void migrateFile(
275                    long companyId, long repositoryId, String fileName,
276                    String versionNumber) {
277    
278                    try {
279                            InputStream is = _sourceStore.getFileAsStream(
280                                    companyId, repositoryId, fileName, versionNumber);
281    
282                            if (versionNumber.equals(Store.VERSION_DEFAULT)) {
283                                    _targetStore.addFile(companyId, repositoryId, fileName, is);
284                            }
285                            else {
286                                    _targetStore.updateFile(
287                                            companyId, repositoryId, fileName, versionNumber, is);
288                            }
289    
290                            if (isDeleteFilesFromSourceStore()) {
291                                    _sourceStore.deleteFile(
292                                            companyId, repositoryId, fileName, versionNumber);
293                            }
294                    }
295                    catch (Exception e) {
296                            _log.error("Migration failed for " + fileName, e);
297                    }
298            }
299    
300            protected void migrateImages() throws PortalException {
301                    int count = ImageLocalServiceUtil.getImagesCount();
302    
303                    MaintenanceUtil.appendStatus("Migrating " + count + " images");
304    
305                    ActionableDynamicQuery actionableDynamicQuery =
306                            ImageLocalServiceUtil.getActionableDynamicQuery();
307    
308                    actionableDynamicQuery.setPerformActionMethod(
309                            new ActionableDynamicQuery.PerformActionMethod() {
310    
311                                    @Override
312                                    public void performAction(Object object) {
313                                            Image image = (Image)object;
314    
315                                            String fileName =
316                                                    image.getImageId() + StringPool.PERIOD +
317                                                            image.getType();
318    
319                                            migrateFile(0, 0, fileName, Store.VERSION_DEFAULT);
320                                    }
321    
322                            });
323    
324                    actionableDynamicQuery.performActions();
325            }
326    
327            protected void migrateMB() throws PortalException {
328                    int count = MBMessageLocalServiceUtil.getMBMessagesCount();
329    
330                    MaintenanceUtil.appendStatus(
331                            "Migrating message boards attachments in " + count + " messages");
332    
333                    ActionableDynamicQuery actionableDynamicQuery =
334                            MBMessageLocalServiceUtil.getActionableDynamicQuery();
335    
336                    actionableDynamicQuery.setPerformActionMethod(
337                            new ActionableDynamicQuery.PerformActionMethod() {
338    
339                                    @Override
340                                    public void performAction(Object object)
341                                            throws PortalException {
342    
343                                            MBMessage mbMessage = (MBMessage)object;
344    
345                                            for (FileEntry fileEntry :
346                                                            mbMessage.getAttachmentsFileEntries()) {
347    
348                                                    DLFileEntry dlFileEntry =
349                                                            (DLFileEntry)fileEntry.getModel();
350    
351                                                    migrateDLFileEntry(
352                                                            mbMessage.getCompanyId(),
353                                                            DLFolderConstants.getDataRepositoryId(
354                                                                    dlFileEntry.getRepositoryId(),
355                                                                    dlFileEntry.getFolderId()),
356                                                            dlFileEntry);
357                                            }
358                                    }
359    
360                            });
361    
362                    actionableDynamicQuery.performActions();
363            }
364    
365            protected void migratePortlets() throws Exception {
366                    migrateImages();
367                    migrateDL();
368                    migrateMB();
369    
370                    Collection<DLStoreConvertProcess> dlStoreConvertProcesses =
371                            _getDLStoreConvertProcesses();
372    
373                    for (DLStoreConvertProcess dlStoreConvertProcess :
374                                    dlStoreConvertProcesses) {
375    
376                            dlStoreConvertProcess.migrate(this);
377                    }
378            }
379    
380            private Collection<DLStoreConvertProcess> _getDLStoreConvertProcesses() {
381                    try {
382                            Registry registry = RegistryUtil.getRegistry();
383    
384                            return registry.getServices(DLStoreConvertProcess.class, null);
385                    }
386                    catch (Exception e) {
387                            throw new SystemException(e);
388                    }
389            }
390    
391            private static final String _FILE_SYSTEM_STORE_SUFFIX = "FileSystemStore";
392    
393            private static final String[] _HOOKS = new String[] {
394                    AdvancedFileSystemStore.class.getName(), CMISStore.class.getName(),
395                    DBStore.class.getName(), FileSystemStore.class.getName(),
396                    JCRStore.class.getName(), S3Store.class.getName()
397            };
398    
399            private static final Log _log = LogFactoryUtil.getLog(
400                    ConvertDocumentLibrary.class);
401    
402            private Store _sourceStore;
403            private Store _targetStore;
404    
405    }