001
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
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 }