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