001/*
002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Antoine Taillefer <ataillefer@nuxeo.com>
018 */
019package org.nuxeo.drive.service.impl;
020
021import org.apache.logging.log4j.LogManager;
022import org.apache.logging.log4j.Logger;
023import org.nuxeo.drive.adapter.FileSystemItem;
024import org.nuxeo.drive.adapter.FolderItem;
025import org.nuxeo.drive.adapter.impl.AbstractFileSystemItem;
026import org.nuxeo.drive.service.FileSystemItemAdapterService;
027import org.nuxeo.drive.service.FileSystemItemFactory;
028import org.nuxeo.ecm.core.api.CloseableCoreSession;
029import org.nuxeo.ecm.core.api.CoreInstance;
030import org.nuxeo.ecm.core.api.CoreSession;
031import org.nuxeo.ecm.core.api.DocumentModel;
032import org.nuxeo.ecm.core.api.DocumentNotFoundException;
033import org.nuxeo.ecm.core.api.DocumentSecurityException;
034import org.nuxeo.ecm.core.api.IdRef;
035import org.nuxeo.ecm.core.api.NuxeoException;
036import org.nuxeo.ecm.core.api.NuxeoPrincipal;
037import org.nuxeo.runtime.api.Framework;
038
039/**
040 * Base class for {@link FileSystemItemFactory} implementers. It is {@link DocumentModel} backed.
041 *
042 * @author Antoine Taillefer
043 * @see DefaultFileSystemItemFactory
044 */
045public abstract class AbstractFileSystemItemFactory implements FileSystemItemFactory {
046
047    private static final Logger log = LogManager.getLogger(AbstractFileSystemItemFactory.class);
048
049    protected String name;
050
051    /**
052     * Adapts the given {@link DocumentModel} to a {@link FileSystemItem}.
053     *
054     * @see #getFileSystemItem(DocumentModel, boolean, FolderItem, boolean, boolean, boolean)
055     */
056    protected abstract FileSystemItem adaptDocument(DocumentModel doc, boolean forceParentItem, FolderItem parentItem,
057            boolean relaxSyncRootConstraint, boolean getLockInfo);
058
059    @Override
060    public String getName() {
061        return name;
062    }
063
064    @Override
065    public void setName(String name) {
066        this.name = name;
067    }
068
069    @Override
070    public boolean isFileSystemItem(DocumentModel doc) {
071        return isFileSystemItem(doc, false);
072    }
073
074    @Override
075    public boolean isFileSystemItem(DocumentModel doc, boolean includeDeleted) {
076        return isFileSystemItem(doc, includeDeleted, false);
077    }
078
079    @Override
080    public FileSystemItem getFileSystemItem(DocumentModel doc) {
081        return getFileSystemItem(doc, false);
082    }
083
084    @Override
085    public FileSystemItem getFileSystemItem(DocumentModel doc, boolean includeDeleted) {
086        return getFileSystemItem(doc, false, null, includeDeleted, false, true);
087    }
088
089    @Override
090    public FileSystemItem getFileSystemItem(DocumentModel doc, boolean includeDeleted,
091            boolean relaxSyncRootConstraint) {
092        return getFileSystemItem(doc, false, null, includeDeleted, relaxSyncRootConstraint, true);
093    }
094
095    @Override
096    public FileSystemItem getFileSystemItem(DocumentModel doc, boolean includeDeleted, boolean relaxSyncRootConstraint,
097            boolean getLockInfo) {
098        return getFileSystemItem(doc, false, null, includeDeleted, relaxSyncRootConstraint, getLockInfo);
099    }
100
101    @Override
102    public FileSystemItem getFileSystemItem(DocumentModel doc, FolderItem parentItem) {
103        return getFileSystemItem(doc, parentItem, false);
104    }
105
106    @Override
107    public FileSystemItem getFileSystemItem(DocumentModel doc, FolderItem parentItem, boolean includeDeleted) {
108        return getFileSystemItem(doc, true, parentItem, includeDeleted, false, true);
109    }
110
111    @Override
112    public FileSystemItem getFileSystemItem(DocumentModel doc, FolderItem parentItem, boolean includeDeleted,
113            boolean relaxSyncRootConstraint) {
114        return getFileSystemItem(doc, true, parentItem, includeDeleted, relaxSyncRootConstraint, true);
115    }
116
117    @Override
118    public FileSystemItem getFileSystemItem(DocumentModel doc, FolderItem parentItem, boolean includeDeleted,
119            boolean relaxSyncRootConstraint, boolean getLockInfo) {
120        return getFileSystemItem(doc, true, parentItem, includeDeleted, relaxSyncRootConstraint, getLockInfo);
121    }
122
123    @Override
124    public boolean canHandleFileSystemItemId(String id) {
125        try {
126            parseFileSystemId(id);
127        } catch (IllegalArgumentException e) {
128            log.trace(e::getMessage);
129            return false;
130        }
131        return true;
132    }
133
134    /**
135     * The default factory considers that a {@link FileSystemItem} with the given id exists if the backing
136     * {@link DocumentModel} can be fetched and {@link #isFileSystemItem(DocumentModel)} returns true.
137     *
138     * @see #isFileSystemItem(DocumentModel)
139     */
140    @Override
141    public boolean exists(String id, NuxeoPrincipal principal) {
142        String[] idFragments = parseFileSystemId(id);
143        String repositoryName = idFragments[1];
144        String docId = idFragments[2];
145        try (CloseableCoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
146            DocumentModel doc = getDocumentById(docId, session);
147            return isFileSystemItem(doc);
148        } catch (DocumentNotFoundException e) {
149            log.debug("No doc related to id {}, returning false.", docId);
150            return false;
151        } catch (DocumentSecurityException e) {
152            log.debug("User {} cannot access doc {}, returning false.", principal, docId);
153            return false;
154        }
155    }
156
157    @Override
158    public FileSystemItem getFileSystemItemById(String id, NuxeoPrincipal principal) {
159        String[] idFragments = parseFileSystemId(id);
160        String repositoryName = idFragments[1];
161        String docId = idFragments[2];
162        try (CloseableCoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
163            DocumentModel doc = getDocumentById(docId, session);
164            return getFileSystemItem(doc);
165        } catch (DocumentNotFoundException e) {
166
167            log.debug("No doc related to id {}, returning null.", docId);
168            return null;
169        } catch (DocumentSecurityException e) {
170            log.debug("User {} cannot access doc {}, returning null.", principal, docId);
171            return null;
172        }
173    }
174
175    @Override
176    public FileSystemItem getFileSystemItemById(String id, String parentId, NuxeoPrincipal principal) {
177        String[] idFragments = parseFileSystemId(id);
178        String repositoryName = idFragments[1];
179        String docId = idFragments[2];
180        try (CloseableCoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
181            FileSystemItem parentItem = Framework.getService(FileSystemItemAdapterService.class)
182                                                 .getFileSystemItemFactoryForId(parentId)
183                                                 .getFileSystemItemById(parentId, principal);
184            if (!(parentItem instanceof FolderItem)) {
185                throw new NuxeoException(String.format("FileSystemItem with id %s should be a FolderItem", parentId));
186            }
187            DocumentModel doc = getDocumentById(docId, session);
188            return getFileSystemItem(doc, (FolderItem) parentItem);
189        } catch (DocumentNotFoundException e) {
190            log.debug("No doc related to id {}, returning null.", docId);
191            return null;
192        } catch (DocumentSecurityException e) {
193            log.debug("User {} cannot access doc {}, returning null.", principal, docId);
194            return null;
195        }
196
197    }
198
199    /*--------------------------- Protected ---------------------------------*/
200    protected FileSystemItem adaptDocument(DocumentModel doc, boolean forceParentItem, FolderItem parentItem) {
201        return adaptDocument(doc, forceParentItem, parentItem, false, true);
202    }
203
204    protected FileSystemItem getFileSystemItem(DocumentModel doc, boolean forceParentItem, FolderItem parentItem,
205            boolean includeDeleted, boolean relaxSyncRootConstraint, boolean getLockInfo) {
206
207        // If the doc is not adaptable as a FileSystemItem return null
208        if (!isFileSystemItem(doc, includeDeleted, relaxSyncRootConstraint)) {
209            log.trace("Document {} cannot be adapted as a FileSystemItem => returning null.", doc::getId);
210            return null;
211        }
212        return adaptDocument(doc, forceParentItem, parentItem, relaxSyncRootConstraint, getLockInfo);
213    }
214
215    protected String[] parseFileSystemId(String id) {
216
217        // Parse id, expecting pattern:
218        // fileSystemItemFactoryName#repositoryName#docId
219        String[] idFragments = id.split(AbstractFileSystemItem.FILE_SYSTEM_ITEM_ID_SEPARATOR);
220        if (idFragments.length != 3) {
221            throw new IllegalArgumentException(String.format(
222                    "FileSystemItem id %s cannot be handled by factory named %s. Should match the 'fileSystemItemFactoryName#repositoryName#docId' pattern.",
223                    id, name));
224        }
225
226        // Check if factory name matches
227        String factoryName = idFragments[0];
228        if (!name.equals(factoryName)) {
229            throw new IllegalArgumentException(
230                    String.format("Factoy name [%s] parsed from id %s does not match the actual factory name [%s].",
231                            factoryName, id, name));
232        }
233        return idFragments;
234    }
235
236    protected DocumentModel getDocumentById(String docId, CoreSession session) {
237        return session.getDocument(new IdRef(docId));
238    }
239
240}