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 java.security.Principal;
022import java.util.Map;
023
024import org.apache.commons.logging.Log;
025import org.apache.commons.logging.LogFactory;
026import org.nuxeo.drive.adapter.FileSystemItem;
027import org.nuxeo.drive.adapter.FolderItem;
028import org.nuxeo.drive.adapter.impl.AbstractFileSystemItem;
029import org.nuxeo.drive.service.FileSystemItemAdapterService;
030import org.nuxeo.drive.service.FileSystemItemFactory;
031import org.nuxeo.drive.service.FileSystemItemManager;
032import org.nuxeo.ecm.core.api.CloseableCoreSession;
033import org.nuxeo.ecm.core.api.CoreInstance;
034import org.nuxeo.ecm.core.api.CoreSession;
035import org.nuxeo.ecm.core.api.DocumentModel;
036import org.nuxeo.ecm.core.api.DocumentNotFoundException;
037import org.nuxeo.ecm.core.api.DocumentSecurityException;
038import org.nuxeo.ecm.core.api.IdRef;
039import org.nuxeo.ecm.core.api.NuxeoException;
040import org.nuxeo.runtime.api.Framework;
041
042/**
043 * Base class for {@link FileSystemItemFactory} implementers. It is {@link DocumentModel} backed.
044 *
045 * @author Antoine Taillefer
046 * @see DefaultFileSystemItemFactory
047 */
048public abstract class AbstractFileSystemItemFactory implements FileSystemItemFactory {
049
050    private static final Log log = LogFactory.getLog(AbstractFileSystemItemFactory.class);
051
052    protected String name;
053
054    /*--------------------------- FileSystemItemFactory ---------------------*/
055    @Override
056    public abstract void handleParameters(Map<String, String> parameters);
057
058    @Override
059    public abstract boolean isFileSystemItem(DocumentModel doc, boolean includeDeleted, boolean relaxSyncRootConstraint);
060
061    /**
062     * Adapts the given {@link DocumentModel} to a {@link FileSystemItem}.
063     *
064     * @see #getFileSystemItem(DocumentModel, boolean, FolderItem, boolean, boolean, boolean)
065     */
066    protected abstract FileSystemItem adaptDocument(DocumentModel doc, boolean forceParentItem, FolderItem parentItem,
067            boolean relaxSyncRootConstraint, boolean getLockInfo);
068
069    @Override
070    public String getName() {
071        return name;
072    }
073
074    @Override
075    public void setName(String name) {
076        this.name = name;
077    }
078
079    @Override
080    public boolean isFileSystemItem(DocumentModel doc) {
081        return isFileSystemItem(doc, false);
082    }
083
084    @Override
085    public boolean isFileSystemItem(DocumentModel doc, boolean includeDeleted) {
086        return isFileSystemItem(doc, includeDeleted, false);
087    }
088
089    @Override
090    public FileSystemItem getFileSystemItem(DocumentModel doc) {
091        return getFileSystemItem(doc, false);
092    }
093
094    @Override
095    public FileSystemItem getFileSystemItem(DocumentModel doc, boolean includeDeleted) {
096        return getFileSystemItem(doc, false, null, includeDeleted, false, true);
097    }
098
099    @Override
100    public FileSystemItem getFileSystemItem(DocumentModel doc, boolean includeDeleted, boolean relaxSyncRootConstraint) {
101        return getFileSystemItem(doc, false, null, includeDeleted, relaxSyncRootConstraint, true);
102    }
103
104    @Override
105    public FileSystemItem getFileSystemItem(DocumentModel doc, boolean includeDeleted, boolean relaxSyncRootConstraint,
106            boolean getLockInfo) {
107        return getFileSystemItem(doc, false, null, includeDeleted, relaxSyncRootConstraint, getLockInfo);
108    }
109
110    @Override
111    public FileSystemItem getFileSystemItem(DocumentModel doc, FolderItem parentItem) {
112        return getFileSystemItem(doc, parentItem, false);
113    }
114
115    @Override
116    public FileSystemItem getFileSystemItem(DocumentModel doc, FolderItem parentItem, boolean includeDeleted) {
117        return getFileSystemItem(doc, true, parentItem, includeDeleted, false, true);
118    }
119
120    @Override
121    public FileSystemItem getFileSystemItem(DocumentModel doc, FolderItem parentItem, boolean includeDeleted,
122            boolean relaxSyncRootConstraint) {
123        return getFileSystemItem(doc, true, parentItem, includeDeleted, relaxSyncRootConstraint, true);
124    }
125
126    @Override
127    public FileSystemItem getFileSystemItem(DocumentModel doc, FolderItem parentItem, boolean includeDeleted,
128            boolean relaxSyncRootConstraint, boolean getLockInfo) {
129        return getFileSystemItem(doc, true, parentItem, includeDeleted, relaxSyncRootConstraint, getLockInfo);
130    }
131
132    @Override
133    public boolean canHandleFileSystemItemId(String id) {
134        try {
135            parseFileSystemId(id);
136        } catch (IllegalArgumentException e) {
137            log.trace(e.getMessage());
138            return false;
139        }
140        return true;
141    }
142
143    /**
144     * The default factory considers that a {@link FileSystemItem} with the given id exists if the backing
145     * {@link DocumentModel} can be fetched and {@link #isFileSystemItem(DocumentModel)} returns true.
146     *
147     * @see #isFileSystemItem(DocumentModel)
148     */
149    @Override
150    public boolean exists(String id, Principal principal) {
151        String[] idFragments = parseFileSystemId(id);
152        String repositoryName = idFragments[1];
153        String docId = idFragments[2];
154        try (CloseableCoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
155            DocumentModel doc = getDocumentById(docId, session);
156            return isFileSystemItem(doc);
157        } catch (DocumentNotFoundException e) {
158            if (log.isDebugEnabled()) {
159                log.debug(String.format("No doc related to id %s, returning false.", docId));
160            }
161            return false;
162        } catch (DocumentSecurityException e) {
163            if (log.isDebugEnabled()) {
164                log.debug(String.format("User %s cannot access doc %s, returning false.", principal.getName(), docId));
165            }
166            return false;
167        }
168    }
169
170    @Override
171    public FileSystemItem getFileSystemItemById(String id, Principal principal) {
172        String[] idFragments = parseFileSystemId(id);
173        String repositoryName = idFragments[1];
174        String docId = idFragments[2];
175        try (CloseableCoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
176            DocumentModel doc = getDocumentById(docId, session);
177            return getFileSystemItem(doc);
178        } catch (DocumentNotFoundException e) {
179            if (log.isDebugEnabled()) {
180                log.debug(String.format("No doc related to id %s, returning null.", docId));
181            }
182            return null;
183        } catch (DocumentSecurityException e) {
184            if (log.isDebugEnabled()) {
185                log.debug(String.format("User %s cannot access doc %s, returning null.", principal.getName(), docId));
186            }
187            return null;
188        }
189    }
190
191    @Override
192    public FileSystemItem getFileSystemItemById(String id, String parentId, Principal principal) {
193        String[] idFragments = parseFileSystemId(id);
194        String repositoryName = idFragments[1];
195        String docId = idFragments[2];
196        try (CloseableCoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
197            FileSystemItem parentItem = Framework.getService(FileSystemItemAdapterService.class)
198                                                 .getFileSystemItemFactoryForId(parentId)
199                                                 .getFileSystemItemById(parentId, principal);
200            if (!(parentItem instanceof FolderItem)) {
201                throw new NuxeoException(String.format("FileSystemItem with id %s should be a FolderItem", parentId));
202            }
203            DocumentModel doc = getDocumentById(docId, session);
204            return getFileSystemItem(doc, (FolderItem) parentItem);
205        } catch (DocumentNotFoundException e) {
206            if (log.isDebugEnabled()) {
207                log.debug(String.format("No doc related to id %s, returning null.", docId));
208            }
209            return null;
210        } catch (DocumentSecurityException e) {
211            if (log.isDebugEnabled()) {
212                log.debug(String.format("User %s cannot access doc %s, returning null.", principal.getName(), docId));
213            }
214            return null;
215        }
216    }
217
218    @Deprecated
219    @Override
220    public DocumentModel getDocumentByFileSystemId(String id, Principal principal) {
221        // Parse id, expecting
222        // pattern:fileSystemItemFactoryName#repositoryName#docId
223        String[] idFragments = parseFileSystemId(id);
224        String repositoryName = idFragments[1];
225        String docId = idFragments[2];
226        CoreSession session = Framework.getService(FileSystemItemManager.class).getSession(repositoryName,
227                principal);
228        return getDocumentById(docId, session);
229    }
230
231    /*--------------------------- Protected ---------------------------------*/
232    protected FileSystemItem adaptDocument(DocumentModel doc, boolean forceParentItem, FolderItem parentItem) {
233        return adaptDocument(doc, forceParentItem, parentItem, false, true);
234    }
235
236    protected FileSystemItem getFileSystemItem(DocumentModel doc, boolean forceParentItem, FolderItem parentItem,
237            boolean includeDeleted, boolean relaxSyncRootConstraint, boolean getLockInfo) {
238
239        // If the doc is not adaptable as a FileSystemItem return null
240        if (!isFileSystemItem(doc, includeDeleted, relaxSyncRootConstraint)) {
241            if (log.isTraceEnabled()) {
242                log.trace(String.format("Document %s cannot be adapted as a FileSystemItem => returning null.",
243                        doc.getId()));
244            }
245            return null;
246        }
247        return adaptDocument(doc, forceParentItem, parentItem, relaxSyncRootConstraint, getLockInfo);
248    }
249
250    protected String[] parseFileSystemId(String id) {
251
252        // Parse id, expecting pattern:
253        // fileSystemItemFactoryName#repositoryName#docId
254        String[] idFragments = id.split(AbstractFileSystemItem.FILE_SYSTEM_ITEM_ID_SEPARATOR);
255        if (idFragments.length != 3) {
256            throw new IllegalArgumentException(
257                    String.format(
258                            "FileSystemItem id %s cannot be handled by factory named %s. Should match the 'fileSystemItemFactoryName#repositoryName#docId' pattern.",
259                            id, name));
260        }
261
262        // Check if factory name matches
263        String factoryName = idFragments[0];
264        if (!name.equals(factoryName)) {
265            throw new IllegalArgumentException(String.format(
266                    "Factoy name [%s] parsed from id %s does not match the actual factory name [%s].", factoryName, id,
267                    name));
268        }
269        return idFragments;
270    }
271
272    protected DocumentModel getDocumentById(String docId, CoreSession session) {
273        return session.getDocument(new IdRef(docId));
274    }
275
276}