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, String, boolean)
064     */
065    protected abstract FileSystemItem adaptDocument(DocumentModel doc, boolean forceParentItem, FolderItem parentItem,
066            boolean relaxSyncRootConstraint);
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);
096    }
097
098    @Override
099    public FileSystemItem getFileSystemItem(DocumentModel doc, boolean includeDeleted, boolean relaxSyncRootConstraint) {
100        return getFileSystemItem(doc, false, null, includeDeleted, relaxSyncRootConstraint);
101    }
102
103    @Override
104    public FileSystemItem getFileSystemItem(DocumentModel doc, FolderItem parentItem) {
105        return getFileSystemItem(doc, parentItem, false);
106    }
107
108    @Override
109    public FileSystemItem getFileSystemItem(DocumentModel doc, FolderItem parentItem, boolean includeDeleted) {
110        return getFileSystemItem(doc, true, parentItem, includeDeleted, false);
111    }
112
113    @Override
114    public FileSystemItem getFileSystemItem(DocumentModel doc, FolderItem parentItem, boolean includeDeleted,
115            boolean relaxSyncRootConstraint) {
116        return getFileSystemItem(doc, true, parentItem, includeDeleted, relaxSyncRootConstraint);
117    }
118
119    @Override
120    public boolean canHandleFileSystemItemId(String id) {
121        try {
122            parseFileSystemId(id);
123        } catch (IllegalArgumentException e) {
124            log.trace(e.getMessage());
125            return false;
126        }
127        return true;
128    }
129
130    /**
131     * The default factory considers that a {@link FileSystemItem} with the given id exists if the backing
132     * {@link DocumentModel} can be fetched and {@link #isFileSystemItem(DocumentModel)} returns true.
133     *
134     * @see #isFileSystemItem(DocumentModel)
135     */
136    @Override
137    public boolean exists(String id, Principal principal) {
138        String[] idFragments = parseFileSystemId(id);
139        String repositoryName = idFragments[1];
140        String docId = idFragments[2];
141        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
142            DocumentModel doc = getDocumentById(docId, session);
143            return isFileSystemItem(doc);
144        } catch (DocumentNotFoundException e) {
145            if (log.isDebugEnabled()) {
146                log.debug(String.format("No doc related to id %s, returning false.", docId));
147            }
148            return false;
149        } catch (DocumentSecurityException e) {
150            if (log.isDebugEnabled()) {
151                log.debug(String.format("User %s cannot access doc %s, returning false.", principal.getName(), docId));
152            }
153            return false;
154        }
155    }
156
157    @Override
158    public FileSystemItem getFileSystemItemById(String id, Principal principal) {
159        String[] idFragments = parseFileSystemId(id);
160        String repositoryName = idFragments[1];
161        String docId = idFragments[2];
162        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
163            DocumentModel doc = getDocumentById(docId, session);
164            return getFileSystemItem(doc);
165        } catch (DocumentNotFoundException e) {
166            if (log.isDebugEnabled()) {
167                log.debug(String.format("No doc related to id %s, returning null.", docId));
168            }
169            return null;
170        } catch (DocumentSecurityException e) {
171            if (log.isDebugEnabled()) {
172                log.debug(String.format("User %s cannot access doc %s, returning null.", principal.getName(), docId));
173            }
174            return null;
175        }
176    }
177
178    @Override
179    public FileSystemItem getFileSystemItemById(String id, String parentId, Principal principal) {
180        String[] idFragments = parseFileSystemId(id);
181        String repositoryName = idFragments[1];
182        String docId = idFragments[2];
183        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
184            FileSystemItem parentItem = Framework.getService(FileSystemItemAdapterService.class).getFileSystemItemFactoryForId(
185                    parentId).getFileSystemItemById(parentId, principal);
186            if (!(parentItem instanceof FolderItem)) {
187                throw new NuxeoException(String.format("FileSystemItem with id %s should be a FolderItem", parentId));
188            }
189            DocumentModel doc = getDocumentById(docId, session);
190            return getFileSystemItem(doc, (FolderItem) parentItem);
191        } catch (DocumentNotFoundException e) {
192            if (log.isDebugEnabled()) {
193                log.debug(String.format("No doc related to id %s, returning null.", docId));
194            }
195            return null;
196        } catch (DocumentSecurityException e) {
197            if (log.isDebugEnabled()) {
198                log.debug(String.format("User %s cannot access doc %s, returning null.", principal.getName(), docId));
199            }
200            return null;
201        }
202    }
203
204    @Deprecated
205    @Override
206    public DocumentModel getDocumentByFileSystemId(String id, Principal principal) {
207        // Parse id, expecting
208        // pattern:fileSystemItemFactoryName#repositoryName#docId
209        String[] idFragments = parseFileSystemId(id);
210        String repositoryName = idFragments[1];
211        String docId = idFragments[2];
212        CoreSession session = Framework.getLocalService(FileSystemItemManager.class).getSession(repositoryName,
213                principal);
214        return getDocumentById(docId, session);
215    }
216
217    /*--------------------------- Protected ---------------------------------*/
218    protected FileSystemItem adaptDocument(DocumentModel doc, boolean forceParentItem, FolderItem parentItem) {
219        return adaptDocument(doc, forceParentItem, parentItem, false);
220    }
221
222    protected FileSystemItem getFileSystemItem(DocumentModel doc, boolean forceParentItem, FolderItem parentItem,
223            boolean includeDeleted, boolean relaxSyncRootConstraint) {
224
225        // If the doc is not adaptable as a FileSystemItem return null
226        if (!isFileSystemItem(doc, includeDeleted, relaxSyncRootConstraint)) {
227            if (log.isTraceEnabled()) {
228                log.trace(String.format("Document %s cannot be adapted as a FileSystemItem => returning null.",
229                        doc.getId()));
230            }
231            return null;
232        }
233        return adaptDocument(doc, forceParentItem, parentItem, relaxSyncRootConstraint);
234    }
235
236    protected String[] parseFileSystemId(String id) {
237
238        // Parse id, expecting pattern:
239        // fileSystemItemFactoryName#repositoryName#docId
240        String[] idFragments = id.split(AbstractFileSystemItem.FILE_SYSTEM_ITEM_ID_SEPARATOR);
241        if (idFragments.length != 3) {
242            throw new IllegalArgumentException(
243                    String.format(
244                            "FileSystemItem id %s cannot be handled by factory named %s. Should match the 'fileSystemItemFactoryName#repositoryName#docId' pattern.",
245                            id, name));
246        }
247
248        // Check if factory name matches
249        String factoryName = idFragments[0];
250        if (!name.equals(factoryName)) {
251            throw new IllegalArgumentException(String.format(
252                    "Factoy name [%s] parsed from id %s does not match the actual factory name [%s].", factoryName, id,
253                    name));
254        }
255        return idFragments;
256    }
257
258    protected DocumentModel getDocumentById(String docId, CoreSession session) {
259        return session.getDocument(new IdRef(docId));
260    }
261
262}