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