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.exception.SystemException;
018    import com.liferay.portal.kernel.util.ArrayUtil;
019    import com.liferay.portal.kernel.util.FileUtil;
020    import com.liferay.portal.kernel.util.StringPool;
021    import com.liferay.portal.kernel.util.Validator;
022    import com.liferay.portal.util.PropsValues;
023    import com.liferay.portlet.documentlibrary.DuplicateFileException;
024    import com.liferay.portlet.documentlibrary.NoSuchFileException;
025    import com.liferay.portlet.documentlibrary.util.DLUtil;
026    
027    import java.io.File;
028    import java.io.FileInputStream;
029    import java.io.FileNotFoundException;
030    import java.io.IOException;
031    import java.io.InputStream;
032    
033    import java.util.ArrayList;
034    import java.util.Collections;
035    import java.util.List;
036    import java.util.Map;
037    import java.util.concurrent.ConcurrentHashMap;
038    
039    /**
040     * @author Brian Wing Shun Chan
041     * @author Sten Martinez
042     * @author Alexander Chow
043     * @author Edward Han
044     */
045    public class FileSystemStore extends BaseStore {
046    
047            public FileSystemStore() {
048                    _rootDir = new File(getRootDirName());
049    
050                    try {
051                            FileUtil.mkdirs(_rootDir);
052                    }
053                    catch (IOException ioe) {
054                            throw new SystemException(ioe);
055                    }
056            }
057    
058            @Override
059            public void addDirectory(
060                    long companyId, long repositoryId, String dirName) {
061    
062                    File dirNameDir = getDirNameDir(companyId, repositoryId, dirName);
063    
064                    if (dirNameDir.exists()) {
065                            return;
066                    }
067    
068                    try {
069                            FileUtil.mkdirs(dirNameDir);
070                    }
071                    catch (IOException ioe) {
072                            throw new SystemException(ioe);
073                    }
074            }
075    
076            @Override
077            public void addFile(
078                            long companyId, long repositoryId, String fileName, InputStream is)
079                    throws DuplicateFileException {
080    
081                    try {
082                            File fileNameVersionFile = getFileNameVersionFile(
083                                    companyId, repositoryId, fileName, VERSION_DEFAULT);
084    
085                            if (fileNameVersionFile.exists()) {
086                                    throw new DuplicateFileException(
087                                            companyId, repositoryId, fileName);
088                            }
089    
090                            FileUtil.write(fileNameVersionFile, is);
091                    }
092                    catch (IOException ioe) {
093                            throw new SystemException(ioe);
094                    }
095            }
096    
097            @Override
098            public void checkRoot(long companyId) {
099            }
100    
101            @Override
102            public void copyFileVersion(
103                            long companyId, long repositoryId, String fileName,
104                            String fromVersionLabel, String toVersionLabel)
105                    throws DuplicateFileException, NoSuchFileException {
106    
107                    File fromFileNameVersionFile = getFileNameVersionFile(
108                            companyId, repositoryId, fileName, fromVersionLabel);
109    
110                    if (!fromFileNameVersionFile.exists()) {
111                            throw new NoSuchFileException(
112                                    companyId, repositoryId, fileName, fromVersionLabel);
113                    }
114    
115                    File toFileNameVersionFile = getFileNameVersionFile(
116                            companyId, repositoryId, fileName, toVersionLabel);
117    
118                    if (toFileNameVersionFile.exists()) {
119                            throw new DuplicateFileException(toFileNameVersionFile.getPath());
120                    }
121    
122                    try {
123                            toFileNameVersionFile.createNewFile();
124    
125                            FileUtil.copyFile(fromFileNameVersionFile, toFileNameVersionFile);
126                    }
127                    catch (IOException ioe) {
128                            throw new SystemException(ioe);
129                    }
130            }
131    
132            @Override
133            public void deleteDirectory(
134                    long companyId, long repositoryId, String dirName) {
135    
136                    File dirNameDir = getDirNameDir(companyId, repositoryId, dirName);
137    
138                    if (!dirNameDir.exists()) {
139                            logFailedDeletion(companyId, repositoryId, dirName);
140    
141                            return;
142                    }
143    
144                    File parentFile = dirNameDir.getParentFile();
145    
146                    FileUtil.deltree(dirNameDir);
147    
148                    RepositoryDirKey repositoryDirKey = new RepositoryDirKey(
149                            companyId, repositoryId);
150    
151                    _repositoryDirs.remove(repositoryDirKey);
152    
153                    deleteEmptyAncestors(parentFile);
154            }
155    
156            @Override
157            public void deleteFile(long companyId, long repositoryId, String fileName) {
158                    File fileNameDir = getFileNameDir(companyId, repositoryId, fileName);
159    
160                    if (!fileNameDir.exists()) {
161                            logFailedDeletion(companyId, repositoryId, fileName);
162    
163                            return;
164                    }
165    
166                    File parentFile = fileNameDir.getParentFile();
167    
168                    FileUtil.deltree(fileNameDir);
169    
170                    deleteEmptyAncestors(companyId, repositoryId, parentFile);
171            }
172    
173            @Override
174            public void deleteFile(
175                    long companyId, long repositoryId, String fileName,
176                    String versionLabel) {
177    
178                    File fileNameVersionFile = getFileNameVersionFile(
179                            companyId, repositoryId, fileName, versionLabel);
180    
181                    if (!fileNameVersionFile.exists()) {
182                            logFailedDeletion(companyId, repositoryId, fileName, versionLabel);
183    
184                            return;
185                    }
186    
187                    File parentFile = fileNameVersionFile.getParentFile();
188    
189                    fileNameVersionFile.delete();
190    
191                    deleteEmptyAncestors(companyId, repositoryId, parentFile);
192            }
193    
194            @Override
195            public File getFile(
196                            long companyId, long repositoryId, String fileName,
197                            String versionLabel)
198                    throws NoSuchFileException {
199    
200                    if (Validator.isNull(versionLabel)) {
201                            versionLabel = getHeadVersionLabel(
202                                    companyId, repositoryId, fileName);
203                    }
204    
205                    File fileNameVersionFile = getFileNameVersionFile(
206                            companyId, repositoryId, fileName, versionLabel);
207    
208                    if (!fileNameVersionFile.exists()) {
209                            throw new NoSuchFileException(
210                                    companyId, repositoryId, fileName, versionLabel);
211                    }
212    
213                    return fileNameVersionFile;
214            }
215    
216            @Override
217            public InputStream getFileAsStream(
218                            long companyId, long repositoryId, String fileName,
219                            String versionLabel)
220                    throws NoSuchFileException {
221    
222                    if (Validator.isNull(versionLabel)) {
223                            versionLabel = getHeadVersionLabel(
224                                    companyId, repositoryId, fileName);
225                    }
226    
227                    File fileNameVersionFile = getFileNameVersionFile(
228                            companyId, repositoryId, fileName, versionLabel);
229    
230                    try {
231                            return new FileInputStream(fileNameVersionFile);
232                    }
233                    catch (FileNotFoundException fnfe) {
234                            throw new NoSuchFileException(
235                                    companyId, repositoryId, fileName, fnfe);
236                    }
237            }
238    
239            @Override
240            public String[] getFileNames(long companyId, long repositoryId) {
241                    File repositoryDir = getRepositoryDir(companyId, repositoryId);
242    
243                    List<String> fileNames = new ArrayList<>();
244    
245                    String[] dirNames = FileUtil.listDirs(repositoryDir);
246    
247                    for (String dirName : dirNames) {
248                            getFileNames(
249                                    fileNames, dirName,
250                                    repositoryDir.getPath() + StringPool.SLASH + dirName);
251                    }
252    
253                    return fileNames.toArray(new String[fileNames.size()]);
254            }
255    
256            @Override
257            public String[] getFileNames(
258                    long companyId, long repositoryId, String dirName) {
259    
260                    File dirNameDir = getDirNameDir(companyId, repositoryId, dirName);
261    
262                    if (!dirNameDir.exists()) {
263                            return new String[0];
264                    }
265    
266                    List<String> fileNames = new ArrayList<>();
267    
268                    getFileNames(fileNames, dirName, dirNameDir.getPath());
269    
270                    Collections.sort(fileNames);
271    
272                    return fileNames.toArray(new String[fileNames.size()]);
273            }
274    
275            @Override
276            public long getFileSize(long companyId, long repositoryId, String fileName)
277                    throws NoSuchFileException {
278    
279                    String versionLabel = getHeadVersionLabel(
280                            companyId, repositoryId, fileName);
281    
282                    File fileNameVersionFile = getFileNameVersionFile(
283                            companyId, repositoryId, fileName, versionLabel);
284    
285                    if (!fileNameVersionFile.exists()) {
286                            throw new NoSuchFileException(companyId, repositoryId, fileName);
287                    }
288    
289                    return fileNameVersionFile.length();
290            }
291    
292            @Override
293            public boolean hasDirectory(
294                    long companyId, long repositoryId, String dirName) {
295    
296                    File dirNameDir = getDirNameDir(companyId, repositoryId, dirName);
297    
298                    return dirNameDir.exists();
299            }
300    
301            @Override
302            public boolean hasFile(
303                    long companyId, long repositoryId, String fileName,
304                    String versionLabel) {
305    
306                    File fileNameVersionFile = getFileNameVersionFile(
307                            companyId, repositoryId, fileName, versionLabel);
308    
309                    return fileNameVersionFile.exists();
310            }
311    
312            @Override
313            public void updateFile(
314                            long companyId, long repositoryId, long newRepositoryId,
315                            String fileName)
316                    throws DuplicateFileException, NoSuchFileException {
317    
318                    File fileNameDir = getFileNameDir(companyId, repositoryId, fileName);
319    
320                    if (!fileNameDir.exists()) {
321                            throw new NoSuchFileException(companyId, repositoryId, fileName);
322                    }
323    
324                    File newFileNameDir = getFileNameDir(
325                            companyId, newRepositoryId, fileName);
326    
327                    if (newFileNameDir.exists()) {
328                            throw new DuplicateFileException(
329                                    companyId, newRepositoryId, fileName);
330                    }
331    
332                    File parentFile = fileNameDir.getParentFile();
333    
334                    boolean renamed = FileUtil.move(fileNameDir, newFileNameDir);
335    
336                    if (!renamed) {
337                            throw new SystemException(
338                                    "File name directory was not renamed from " +
339                                            fileNameDir.getPath() + " to " + newFileNameDir.getPath());
340                    }
341    
342                    deleteEmptyAncestors(companyId, repositoryId, parentFile);
343            }
344    
345            @Override
346            public void updateFile(
347                            long companyId, long repositoryId, String fileName,
348                            String newFileName)
349                    throws DuplicateFileException, NoSuchFileException {
350    
351                    File fileNameDir = getFileNameDir(companyId, repositoryId, fileName);
352    
353                    if (!fileNameDir.exists()) {
354                            throw new NoSuchFileException(companyId, repositoryId, fileName);
355                    }
356    
357                    File newFileNameDir = getFileNameDir(
358                            companyId, repositoryId, newFileName);
359    
360                    if (newFileNameDir.exists()) {
361                            throw new DuplicateFileException(
362                                    companyId, repositoryId, newFileName);
363                    }
364    
365                    File parentFile = fileNameDir.getParentFile();
366    
367                    boolean renamed = FileUtil.move(fileNameDir, newFileNameDir);
368    
369                    if (!renamed) {
370                            throw new SystemException(
371                                    "File name directory was not renamed from " +
372                                            fileNameDir.getPath() + " to " + newFileNameDir.getPath());
373                    }
374    
375                    deleteEmptyAncestors(companyId, repositoryId, parentFile);
376            }
377    
378            @Override
379            public void updateFile(
380                            long companyId, long repositoryId, String fileName,
381                            String versionLabel, InputStream is)
382                    throws DuplicateFileException {
383    
384                    try {
385                            File fileNameVersionFile = getFileNameVersionFile(
386                                    companyId, repositoryId, fileName, versionLabel);
387    
388                            if (fileNameVersionFile.exists()) {
389                                    throw new DuplicateFileException(
390                                            companyId, repositoryId, fileName, versionLabel);
391                            }
392    
393                            FileUtil.write(fileNameVersionFile, is);
394                    }
395                    catch (IOException ioe) {
396                            throw new SystemException(ioe);
397                    }
398            }
399    
400            @Override
401            public void updateFileVersion(
402                            long companyId, long repositoryId, String fileName,
403                            String fromVersionLabel, String toVersionLabel)
404                    throws DuplicateFileException, NoSuchFileException {
405    
406                    File fromFileNameVersionFile = getFileNameVersionFile(
407                            companyId, repositoryId, fileName, fromVersionLabel);
408    
409                    if (!fromFileNameVersionFile.exists()) {
410                            throw new NoSuchFileException(
411                                    companyId, repositoryId, fileName, fromVersionLabel);
412                    }
413    
414                    File toFileNameVersionFile = getFileNameVersionFile(
415                            companyId, repositoryId, fileName, toVersionLabel);
416    
417                    if (toFileNameVersionFile.exists()) {
418                            throw new DuplicateFileException(
419                                    companyId, repositoryId, fileName, toVersionLabel);
420                    }
421    
422                    boolean renamed = FileUtil.move(
423                            fromFileNameVersionFile, toFileNameVersionFile);
424    
425                    if (!renamed) {
426                            throw new SystemException(
427                                    "File name version file was not renamed from " +
428                                            fromFileNameVersionFile.getPath() + " to " +
429                                                    toFileNameVersionFile.getPath());
430                    }
431            }
432    
433            protected void deleteEmptyAncestors(File file) {
434                    deleteEmptyAncestors(-1, -1, file);
435            }
436    
437            protected void deleteEmptyAncestors(
438                    long companyId, long repositoryId, File file) {
439    
440                    String[] fileNames = file.list();
441    
442                    if ((fileNames == null) || (fileNames.length > 0)) {
443                            return;
444                    }
445    
446                    String fileName = file.getName();
447    
448                    if ((repositoryId > 0) &&
449                            fileName.equals(String.valueOf(repositoryId))) {
450    
451                            RepositoryDirKey repositoryDirKey = new RepositoryDirKey(
452                                    companyId, repositoryId);
453    
454                            _repositoryDirs.remove(repositoryDirKey);
455                    }
456    
457                    File parentFile = file.getParentFile();
458    
459                    if (file.delete() && (parentFile != null)) {
460                            deleteEmptyAncestors(companyId, repositoryId, parentFile);
461                    }
462            }
463    
464            protected File getCompanyDir(long companyId) {
465                    File companyDir = new File(_rootDir + StringPool.SLASH + companyId);
466    
467                    try {
468                            FileUtil.mkdirs(companyDir);
469                    }
470                    catch (IOException ioe) {
471                            throw new SystemException(ioe);
472                    }
473    
474                    return companyDir;
475            }
476    
477            protected File getDirNameDir(
478                    long companyId, long repositoryId, String dirName) {
479    
480                    return getFileNameDir(companyId, repositoryId, dirName);
481            }
482    
483            protected File getFileNameDir(
484                    long companyId, long repositoryId, String fileName) {
485    
486                    File repositoryDir = getRepositoryDir(companyId, repositoryId);
487    
488                    File fileNameDir = new File(
489                            repositoryDir + StringPool.SLASH + fileName);
490    
491                    return fileNameDir;
492            }
493    
494            protected void getFileNames(
495                    List<String> fileNames, String dirName, String path) {
496    
497                    String[] pathDirNames = FileUtil.listDirs(path);
498    
499                    if (ArrayUtil.isNotEmpty(pathDirNames)) {
500                            for (String pathDirName : pathDirNames) {
501                                    String subdirName = null;
502    
503                                    if (Validator.isBlank(dirName)) {
504                                            subdirName = pathDirName;
505                                    }
506                                    else {
507                                            subdirName = dirName + StringPool.SLASH + pathDirName;
508                                    }
509    
510                                    getFileNames(
511                                            fileNames, subdirName,
512                                            path + StringPool.SLASH + pathDirName);
513                            }
514                    }
515                    else {
516                            fileNames.add(dirName);
517                    }
518            }
519    
520            protected File getFileNameVersionFile(
521                    long companyId, long repositoryId, String fileName, String version) {
522    
523                    File fileNameDir = getFileNameDir(companyId, repositoryId, fileName);
524    
525                    File fileNameVersionFile = new File(
526                            fileNameDir + StringPool.SLASH + version);
527    
528                    return fileNameVersionFile;
529            }
530    
531            protected String getHeadVersionLabel(
532                    long companyId, long repositoryId, String fileName) {
533    
534                    File fileNameDir = getFileNameDir(companyId, repositoryId, fileName);
535    
536                    if (!fileNameDir.exists()) {
537                            return VERSION_DEFAULT;
538                    }
539    
540                    String[] versionLabels = FileUtil.listFiles(fileNameDir);
541    
542                    String headVersionLabel = VERSION_DEFAULT;
543    
544                    for (String versionLabel : versionLabels) {
545                            if (DLUtil.compareVersions(versionLabel, headVersionLabel) > 0) {
546                                    headVersionLabel = versionLabel;
547                            }
548                    }
549    
550                    return headVersionLabel;
551            }
552    
553            protected File getRepositoryDir(long companyId, long repositoryId) {
554                    RepositoryDirKey repositoryDirKey = new RepositoryDirKey(
555                            companyId, repositoryId);
556    
557                    File repositoryDir = _repositoryDirs.get(repositoryDirKey);
558    
559                    if (repositoryDir == null) {
560                            File companyDir = getCompanyDir(companyId);
561    
562                            repositoryDir = new File(
563                                    companyDir + StringPool.SLASH + repositoryId);
564    
565                            try {
566                                    FileUtil.mkdirs(repositoryDir);
567                            }
568                            catch (IOException ioe) {
569                                    throw new SystemException(ioe);
570                            }
571    
572                            _repositoryDirs.put(repositoryDirKey, repositoryDir);
573                    }
574    
575                    return repositoryDir;
576            }
577    
578            protected String getRootDirName() {
579                    return PropsValues.DL_STORE_FILE_SYSTEM_ROOT_DIR;
580            }
581    
582            private final Map<RepositoryDirKey, File> _repositoryDirs =
583                    new ConcurrentHashMap<>();
584            private final File _rootDir;
585    
586            private class RepositoryDirKey {
587    
588                    public RepositoryDirKey(long companyId, long repositoryId) {
589                            _companyId = companyId;
590                            _repositoryId = repositoryId;
591                    }
592    
593                    @Override
594                    public boolean equals(Object obj) {
595                            RepositoryDirKey repositoryDirKey = (RepositoryDirKey)obj;
596    
597                            if ((_companyId == repositoryDirKey._companyId) &&
598                                    (_repositoryId == repositoryDirKey._repositoryId)) {
599    
600                                    return true;
601                            }
602                            else {
603                                    return false;
604                            }
605                    }
606    
607                    @Override
608                    public int hashCode() {
609                            return (int)(_companyId * 11 + _repositoryId);
610                    }
611    
612                    private long _companyId;
613                    private long _repositoryId;
614    
615            }
616    
617    }