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