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.portlet.documentlibrary.store;
016    
017    import com.liferay.portal.kernel.util.CharPool;
018    import com.liferay.portal.kernel.util.ContentTypes;
019    import com.liferay.portal.kernel.util.ListUtil;
020    import com.liferay.portal.kernel.util.LocaleUtil;
021    import com.liferay.portal.kernel.util.StringPool;
022    import com.liferay.portal.kernel.util.StringUtil;
023    import com.liferay.portal.kernel.util.Validator;
024    import com.liferay.portal.repository.cmis.CMISRepositoryUtil;
025    import com.liferay.portal.util.PropsValues;
026    import com.liferay.portlet.documentlibrary.DuplicateFileException;
027    import com.liferay.portlet.documentlibrary.NoSuchFileException;
028    import com.liferay.portlet.documentlibrary.util.DLUtil;
029    
030    import java.io.InputStream;
031    
032    import java.util.ArrayList;
033    import java.util.HashMap;
034    import java.util.List;
035    import java.util.Locale;
036    import java.util.Map;
037    
038    import org.apache.chemistry.opencmis.client.api.CmisObject;
039    import org.apache.chemistry.opencmis.client.api.Document;
040    import org.apache.chemistry.opencmis.client.api.Folder;
041    import org.apache.chemistry.opencmis.client.api.ItemIterable;
042    import org.apache.chemistry.opencmis.client.api.ObjectId;
043    import org.apache.chemistry.opencmis.client.api.Repository;
044    import org.apache.chemistry.opencmis.client.api.Session;
045    import org.apache.chemistry.opencmis.client.api.SessionFactory;
046    import org.apache.chemistry.opencmis.client.runtime.ObjectIdImpl;
047    import org.apache.chemistry.opencmis.commons.PropertyIds;
048    import org.apache.chemistry.opencmis.commons.SessionParameter;
049    import org.apache.chemistry.opencmis.commons.data.ContentStream;
050    import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
051    import org.apache.chemistry.opencmis.commons.enums.BindingType;
052    import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
053    import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
054    
055    /**
056     * @author Alexander Chow
057     * @author Edward Han
058     */
059    public class CMISStore extends BaseStore {
060    
061            public CMISStore() {
062                    Folder systemRootDir = getFolder(
063                            SessionHolder.session.getRootFolder(),
064                            PropsValues.DL_STORE_CMIS_SYSTEM_ROOT_DIR);
065    
066                    if (systemRootDir == null) {
067                            systemRootDir = createFolder(
068                                    SessionHolder.session.getRootFolder(),
069                                    PropsValues.DL_STORE_CMIS_SYSTEM_ROOT_DIR);
070                    }
071    
072                    _systemRootDir = systemRootDir;
073            }
074    
075            @Override
076            public void addDirectory(
077                    long companyId, long repositoryId, String dirName) {
078    
079                    Folder folder = getRepositoryFolder(companyId, repositoryId);
080    
081                    String[] dirNames = StringUtil.split(dirName, CharPool.SLASH);
082    
083                    for (String curDirName : dirNames) {
084                            Folder subFolder = getFolder(folder, curDirName);
085    
086                            if (subFolder == null) {
087                                    subFolder = createFolder(folder, curDirName);
088                            }
089    
090                            folder = subFolder;
091                    }
092            }
093    
094            @Override
095            public void addFile(
096                            long companyId, long repositoryId, String fileName, InputStream is)
097                    throws DuplicateFileException {
098    
099                    updateFile(companyId, repositoryId, fileName, VERSION_DEFAULT, is);
100            }
101    
102            @Override
103            public void checkRoot(long companyId) {
104            }
105    
106            @Override
107            public void copyFileVersion(
108                            long companyId, long repositoryId, String fileName,
109                            String fromVersionLabel, String toVersionLabel)
110                    throws DuplicateFileException, NoSuchFileException {
111    
112                    Folder versioningFolder = getVersioningFolder(
113                            companyId, repositoryId, fileName, false);
114    
115                    if (versioningFolder == null) {
116                            throw new NoSuchFileException(
117                                    companyId, repositoryId, fileName, fromVersionLabel);
118                    }
119    
120                    if (hasFile(companyId, repositoryId, fileName, toVersionLabel)) {
121                            throw new DuplicateFileException(
122                                    companyId, repositoryId, fileName, toVersionLabel);
123                    }
124    
125                    ObjectId versioningFolderObjectId = new ObjectIdImpl(
126                            versioningFolder.getId());
127    
128                    Map<String, Object> documentProperties = new HashMap<>();
129    
130                    documentProperties.put(PropertyIds.NAME, toVersionLabel);
131    
132                    documentProperties.put(
133                            PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value());
134    
135                    Document document = getVersionedDocument(
136                            companyId, repositoryId, fileName, fromVersionLabel);
137    
138                    document.copy(
139                            versioningFolderObjectId, documentProperties, null,
140                            document.getPolicies(), null, null, null);
141            }
142    
143            @Override
144            public void deleteDirectory(
145                    long companyId, long repositoryId, String dirName) {
146    
147                    Folder repositoryFolder = getRepositoryFolder(companyId, repositoryId);
148    
149                    Folder directory = getFolder(repositoryFolder, dirName);
150    
151                    if (directory != null) {
152                            directory.deleteTree(true, UnfileObject.DELETE, false);
153                    }
154            }
155    
156            @Override
157            public void deleteFile(long companyId, long repositoryId, String fileName) {
158                    Folder versioningFolder = getVersioningFolder(
159                            companyId, repositoryId, fileName, false);
160    
161                    if (versioningFolder == null) {
162                            logFailedDeletion(companyId, repositoryId, fileName);
163                    }
164                    else {
165                            versioningFolder.deleteTree(true, UnfileObject.DELETE, false);
166                    }
167            }
168    
169            @Override
170            public void deleteFile(
171                    long companyId, long repositoryId, String fileName,
172                    String versionLabel) {
173    
174                    Document document = null;
175    
176                    try {
177                            document = getVersionedDocument(
178                                    companyId, repositoryId, fileName, versionLabel);
179    
180                            document.delete(true);
181                    }
182                    catch (NoSuchFileException nsfe) {
183                            logFailedDeletion(companyId, repositoryId, fileName, versionLabel);
184                    }
185            }
186    
187            @Override
188            public InputStream getFileAsStream(
189                            long companyId, long repositoryId, String fileName,
190                            String versionLabel)
191                    throws NoSuchFileException {
192    
193                    if (Validator.isNull(versionLabel)) {
194                            versionLabel = getHeadVersionLabel(
195                                    companyId, repositoryId, fileName);
196                    }
197    
198                    Document document = getVersionedDocument(
199                            companyId, repositoryId, fileName, versionLabel);
200    
201                    return document.getContentStream().getStream();
202            }
203    
204            @Override
205            public String[] getFileNames(long companyId, long repositoryId) {
206                    Folder folder = getRepositoryFolder(companyId, repositoryId);
207    
208                    List<String> fileNames = new ArrayList<>();
209    
210                    doGetFileNames(fileNames, StringPool.BLANK, folder);
211    
212                    return fileNames.toArray(new String[fileNames.size()]);
213            }
214    
215            @Override
216            public String[] getFileNames(
217                    long companyId, long repositoryId, String dirName) {
218    
219                    Folder directory = getRepositoryFolder(companyId, repositoryId);
220    
221                    String[] subFolderNames = StringUtil.split(dirName, StringPool.SLASH);
222    
223                    for (String subFolderName : subFolderNames) {
224                            directory = getFolder(directory, subFolderName);
225    
226                            if (directory == null) {
227                                    return new String[0];
228                            }
229                    }
230    
231                    List<String> fileNames = new ArrayList<>();
232    
233                    doGetFileNames(fileNames, dirName, directory);
234    
235                    return fileNames.toArray(new String[fileNames.size()]);
236            }
237    
238            @Override
239            public long getFileSize(long companyId, long repositoryId, String fileName)
240                    throws NoSuchFileException {
241    
242                    String versionLabel = getHeadVersionLabel(
243                            companyId, repositoryId, fileName);
244    
245                    Document document = getVersionedDocument(
246                            companyId, repositoryId, fileName, versionLabel);
247    
248                    return document.getContentStreamLength();
249            }
250    
251            public String getHeadVersionLabel(
252                            long companyId, long repositoryId, String dirName)
253                    throws NoSuchFileException {
254    
255                    Folder versioningFolder = getVersioningFolder(
256                            companyId, repositoryId, dirName, false);
257    
258                    if (versioningFolder == null) {
259                            throw new NoSuchFileException(companyId, repositoryId, dirName);
260                    }
261    
262                    String headVersionLabel = VERSION_DEFAULT;
263    
264                    List<String> versionLabels = getVersionLabels(versioningFolder);
265    
266                    for (String versionLabel : versionLabels) {
267                            if (DLUtil.compareVersions(versionLabel, headVersionLabel) > 0) {
268                                    headVersionLabel = versionLabel;
269                            }
270                    }
271    
272                    return headVersionLabel;
273            }
274    
275            @Override
276            public boolean hasDirectory(
277                    long companyId, long repositoryId, String dirName) {
278    
279                    Folder folder = getRepositoryFolder(companyId, repositoryId);
280    
281                    String[] dirNames = StringUtil.split(dirName, CharPool.SLASH);
282    
283                    for (String subdirName : dirNames) {
284                            Folder subfolder = getFolder(folder, subdirName);
285    
286                            if (subfolder == null) {
287                                    return false;
288                            }
289                    }
290    
291                    return true;
292            }
293    
294            @Override
295            public boolean hasFile(
296                    long companyId, long repositoryId, String fileName,
297                    String versionLabel) {
298    
299                    Folder versioningFolder = getVersioningFolder(
300                            companyId, repositoryId, fileName, true);
301    
302                    Document document = getDocument(versioningFolder, versionLabel);
303    
304                    if (document == null) {
305                            return false;
306                    }
307                    else {
308                            return true;
309                    }
310            }
311    
312            @Override
313            public void updateFile(
314                            long companyId, long repositoryId, long newRepositoryId,
315                            String fileName)
316                    throws DuplicateFileException, NoSuchFileException {
317    
318                    Folder oldVersioningFolderEntry = getVersioningFolder(
319                            companyId, repositoryId, fileName, false);
320    
321                    if (oldVersioningFolderEntry == null) {
322                            throw new NoSuchFileException(companyId, repositoryId, fileName);
323                    }
324    
325                    if (hasFile(companyId, newRepositoryId, fileName)) {
326                            throw new DuplicateFileException(
327                                    companyId, newRepositoryId, fileName);
328                    }
329    
330                    Folder newVersioningFolderEntry = getVersioningFolder(
331                            companyId, newRepositoryId, fileName, true);
332    
333                    List<String> versionLabels = getVersionLabels(oldVersioningFolderEntry);
334    
335                    for (String versionLabel : versionLabels) {
336                            Document document = getDocument(
337                                    oldVersioningFolderEntry, versionLabel);
338    
339                            InputStream is = document.getContentStream().getStream();
340    
341                            createDocument(newVersioningFolderEntry, versionLabel, is);
342                    }
343    
344                    oldVersioningFolderEntry.deleteTree(true, UnfileObject.DELETE, false);
345            }
346    
347            @Override
348            public void updateFile(
349                            long companyId, long repositoryId, String fileName,
350                            String newFileName)
351                    throws DuplicateFileException, NoSuchFileException {
352    
353                    Folder oldVersioningFolderEntry = getVersioningFolder(
354                            companyId, repositoryId, fileName, false);
355    
356                    if (oldVersioningFolderEntry == null) {
357                            throw new NoSuchFileException(companyId, repositoryId, fileName);
358                    }
359    
360                    if (hasFile(companyId, repositoryId, newFileName)) {
361                            throw new DuplicateFileException(
362                                    companyId, repositoryId, newFileName);
363                    }
364    
365                    Folder newVersioningFolderEntry = getVersioningFolder(
366                            companyId, repositoryId, newFileName, true);
367    
368                    List<String> versionLabels = getVersionLabels(oldVersioningFolderEntry);
369    
370                    for (String versionLabel : versionLabels) {
371                            Document document = getDocument(
372                                    oldVersioningFolderEntry, versionLabel);
373    
374                            InputStream is = document.getContentStream().getStream();
375    
376                            createDocument(newVersioningFolderEntry, versionLabel, is);
377                    }
378    
379                    oldVersioningFolderEntry.deleteTree(true, UnfileObject.DELETE, false);
380            }
381    
382            @Override
383            public void updateFile(
384                            long companyId, long repositoryId, String fileName,
385                            String versionLabel, InputStream is)
386                    throws DuplicateFileException {
387    
388                    Folder versioningFolder = getVersioningFolder(
389                            companyId, repositoryId, fileName, true);
390    
391                    String title = String.valueOf(versionLabel);
392    
393                    Document document = getDocument(versioningFolder, title);
394    
395                    if (document != null) {
396                            throw new DuplicateFileException(
397                                    companyId, repositoryId, fileName, versionLabel);
398                    }
399    
400                    createDocument(versioningFolder, title, is);
401            }
402    
403            @Override
404            public void updateFileVersion(
405                            long companyId, long repositoryId, String fileName,
406                            String fromVersionLabel, String toVersionLabel)
407                    throws DuplicateFileException, NoSuchFileException {
408    
409                    Folder versioningFolder = getVersioningFolder(
410                            companyId, repositoryId, fileName, false);
411    
412                    if (versioningFolder == null) {
413                            throw new NoSuchFileException(
414                                    companyId, repositoryId, fileName, fromVersionLabel);
415                    }
416    
417                    Document document = getDocument(versioningFolder, toVersionLabel);
418    
419                    if (document != null) {
420                            throw new DuplicateFileException(
421                                    companyId, repositoryId, fileName, toVersionLabel);
422                    }
423    
424                    document = getVersionedDocument(
425                            companyId, repositoryId, fileName, fromVersionLabel);
426    
427                    Map<String, Object> documentProperties = new HashMap<>();
428    
429                    documentProperties.put(PropertyIds.NAME, toVersionLabel);
430    
431                    document.updateProperties(documentProperties);
432            }
433    
434            protected Document createDocument(
435                    Folder versioningFolder, String title, InputStream is) {
436    
437                    Map<String, Object> documentProperties = new HashMap<>();
438    
439                    documentProperties.put(PropertyIds.NAME, title);
440                    documentProperties.put(
441                            PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value());
442    
443                    ContentStream contentStream = new ContentStreamImpl(
444                            null, null, ContentTypes.APPLICATION_OCTET_STREAM, is);
445    
446                    return versioningFolder.createDocument(
447                            documentProperties, contentStream, null);
448            }
449    
450            protected Folder createFolder(ObjectId parentFolderId, String name) {
451                    Map<String, Object> properties = new HashMap<>();
452    
453                    properties.put(PropertyIds.NAME, name);
454                    properties.put(
455                            PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_FOLDER.value());
456    
457                    ObjectId objectId = SessionHolder.session.createFolder(
458                            properties, parentFolderId);
459    
460                    return (Folder)SessionHolder.session.getObject(objectId);
461            }
462    
463            protected void doGetFileNames(
464                    List<String> fileNames, String dirName, Folder folder) {
465    
466                    List<Folder> folders = getFolders(folder);
467    
468                    if (ListUtil.isNotEmpty(folders)) {
469                            for (Folder curFolder : folders) {
470                                    String subDirName = null;
471    
472                                    if (Validator.isBlank(dirName)) {
473                                            subDirName = curFolder.getName();
474                                    }
475                                    else {
476                                            subDirName =
477                                                    dirName + StringPool.SLASH + curFolder.getName();
478                                    }
479    
480                                    doGetFileNames(fileNames, subDirName, curFolder);
481                            }
482                    }
483                    else {
484                            fileNames.add(dirName);
485                    }
486            }
487    
488            protected Folder getCompanyFolder(long companyId) {
489                    String name = String.valueOf(companyId);
490    
491                    Folder companyFolder = getFolder(_systemRootDir, name);
492    
493                    if (companyFolder == null) {
494                            companyFolder = createFolder(_systemRootDir, name);
495                    }
496    
497                    return companyFolder;
498            }
499    
500            protected Document getDocument(Folder parentFolder, String name) {
501                    ItemIterable<CmisObject> cmisObjects = parentFolder.getChildren();
502    
503                    for (CmisObject cmisObject : cmisObjects) {
504                            if (name.equals(cmisObject.getName()) &&
505                                    cmisObject instanceof Document) {
506    
507                                    return (Document)cmisObject;
508                            }
509                    }
510    
511                    return null;
512            }
513    
514            protected Folder getFolder(Folder parentFolder, String name) {
515                    ItemIterable<CmisObject> cmisObjects = parentFolder.getChildren();
516    
517                    for (CmisObject cmisObject : cmisObjects) {
518                            if (name.equals(cmisObject.getName()) &&
519                                    cmisObject instanceof Folder) {
520    
521                                    return (Folder)cmisObject;
522                            }
523                    }
524    
525                    return null;
526            }
527    
528            protected List<Folder> getFolders(Folder folder) {
529                    List<Folder> folders = new ArrayList<>();
530    
531                    ItemIterable<CmisObject> cmisObjects = folder.getChildren();
532    
533                    for (CmisObject cmisObject : cmisObjects) {
534                            if (cmisObject instanceof Folder) {
535                                    folders.add((Folder)cmisObject);
536                            }
537                    }
538    
539                    return folders;
540            }
541    
542            protected Folder getRepositoryFolder(long companyId, long repositoryId) {
543                    Folder companyFolder = getCompanyFolder(companyId);
544    
545                    String name = String.valueOf(repositoryId);
546    
547                    Folder repositoryFolder = getFolder(companyFolder, name);
548    
549                    if (repositoryFolder == null) {
550                            repositoryFolder = createFolder(companyFolder, name);
551                    }
552    
553                    return repositoryFolder;
554            }
555    
556            protected Document getVersionedDocument(
557                            long companyId, long repositoryId, String fileName,
558                            String versionLabel)
559                    throws NoSuchFileException {
560    
561                    Folder versioningFolder = getVersioningFolder(
562                            companyId, repositoryId, fileName, false);
563    
564                    if (versioningFolder == null) {
565                            throw new NoSuchFileException(companyId, repositoryId, fileName);
566                    }
567    
568                    Document document = getDocument(versioningFolder, versionLabel);
569    
570                    if (document == null) {
571                            throw new NoSuchFileException(
572                                    companyId, repositoryId, fileName, versionLabel);
573                    }
574    
575                    return document;
576            }
577    
578            protected Folder getVersioningFolder(
579                    long companyId, long repositoryId, String fileName, boolean create) {
580    
581                    Folder repositoryFolder = getRepositoryFolder(companyId, repositoryId);
582    
583                    Folder versioningFolder = repositoryFolder;
584    
585                    String[] dirNames = StringUtil.split(fileName, CharPool.SLASH);
586    
587                    for (String dirName : dirNames) {
588                            Folder subFolder = getFolder(versioningFolder, dirName);
589    
590                            if (create && (subFolder == null)) {
591                                    subFolder = createFolder(versioningFolder, dirName);
592                            }
593    
594                            versioningFolder = subFolder;
595                    }
596    
597                    return versioningFolder;
598            }
599    
600            protected List<String> getVersionLabels(Folder versioningFolder) {
601                    List<String> versions = new ArrayList<>();
602    
603                    ItemIterable<CmisObject> cmisObjects = versioningFolder.getChildren();
604    
605                    for (CmisObject cmisObject : cmisObjects) {
606                            if (cmisObject instanceof Document) {
607                                    versions.add(cmisObject.getName());
608                            }
609                    }
610    
611                    return versions;
612            }
613    
614            private final Folder _systemRootDir;
615    
616            private static class SessionHolder {
617    
618                    private static final Session session;
619    
620                    static {
621                            Map<String, String> parameters = new HashMap<>();
622    
623                            parameters.put(
624                                    SessionParameter.ATOMPUB_URL,
625                                    PropsValues.DL_STORE_CMIS_REPOSITORY_URL);
626                            parameters.put(
627                                    SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
628                            parameters.put(
629                                    SessionParameter.COMPRESSION, Boolean.TRUE.toString());
630    
631                            Locale locale = LocaleUtil.getDefault();
632    
633                            parameters.put(
634                                    SessionParameter.LOCALE_ISO3166_COUNTRY, locale.getCountry());
635                            parameters.put(
636                                    SessionParameter.LOCALE_ISO639_LANGUAGE, locale.getLanguage());
637                            parameters.put(
638                                    SessionParameter.PASSWORD,
639                                    PropsValues.DL_STORE_CMIS_CREDENTIALS_PASSWORD);
640                            parameters.put(
641                                    SessionParameter.USER,
642                                    PropsValues.DL_STORE_CMIS_CREDENTIALS_USERNAME);
643    
644                            SessionFactory sessionFactory =
645                                    CMISRepositoryUtil.getSessionFactory();
646    
647                            List<Repository> repositories = sessionFactory.getRepositories(
648                                    parameters);
649    
650                            Repository repository = repositories.get(0);
651    
652                            session = repository.createSession();
653    
654                            session.setDefaultContext(CMISRepositoryUtil.getOperationContext());
655                    }
656    
657            }
658    
659    }