001/*
002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Antoine Taillefer <ataillefer@nuxeo.com>
016 */
017package org.nuxeo.drive.service.impl;
018
019import java.security.Principal;
020import java.util.Map;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024import org.nuxeo.drive.adapter.FileSystemItem;
025import org.nuxeo.drive.adapter.FolderItem;
026import org.nuxeo.drive.adapter.impl.AbstractFileSystemItem;
027import org.nuxeo.drive.service.FileSystemItemAdapterService;
028import org.nuxeo.drive.service.FileSystemItemFactory;
029import org.nuxeo.drive.service.FileSystemItemManager;
030import org.nuxeo.ecm.core.api.CoreInstance;
031import org.nuxeo.ecm.core.api.CoreSession;
032import org.nuxeo.ecm.core.api.DocumentModel;
033import org.nuxeo.ecm.core.api.DocumentNotFoundException;
034import org.nuxeo.ecm.core.api.DocumentSecurityException;
035import org.nuxeo.ecm.core.api.IdRef;
036import org.nuxeo.ecm.core.api.NuxeoException;
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 Log log = LogFactory.getLog(AbstractFileSystemItemFactory.class);
048
049    protected String name;
050
051    /*--------------------------- FileSystemItemFactory ---------------------*/
052    @Override
053    public abstract void handleParameters(Map<String, String> parameters);
054
055    @Override
056    public abstract boolean isFileSystemItem(DocumentModel doc, boolean includeDeleted, boolean relaxSyncRootConstraint);
057
058    /**
059     * Adapts the given {@link DocumentModel} to a {@link FileSystemItem}.
060     *
061     * @see #getFileSystemItem(DocumentModel, boolean, String, boolean)
062     */
063    protected abstract FileSystemItem adaptDocument(DocumentModel doc, boolean forceParentItem, FolderItem parentItem,
064            boolean relaxSyncRootConstraint);
065
066    @Override
067    public String getName() {
068        return name;
069    }
070
071    @Override
072    public void setName(String name) {
073        this.name = name;
074    }
075
076    @Override
077    public boolean isFileSystemItem(DocumentModel doc) {
078        return isFileSystemItem(doc, false);
079    }
080
081    @Override
082    public boolean isFileSystemItem(DocumentModel doc, boolean includeDeleted) {
083        return isFileSystemItem(doc, includeDeleted, false);
084    }
085
086    @Override
087    public FileSystemItem getFileSystemItem(DocumentModel doc) {
088        return getFileSystemItem(doc, false);
089    }
090
091    @Override
092    public FileSystemItem getFileSystemItem(DocumentModel doc, boolean includeDeleted) {
093        return getFileSystemItem(doc, false, null, includeDeleted, false);
094    }
095
096    @Override
097    public FileSystemItem getFileSystemItem(DocumentModel doc, boolean includeDeleted, boolean relaxSyncRootConstraint) {
098        return getFileSystemItem(doc, false, null, includeDeleted, relaxSyncRootConstraint);
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);
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);
115    }
116
117    @Override
118    public boolean canHandleFileSystemItemId(String id) {
119        try {
120            parseFileSystemId(id);
121        } catch (IllegalArgumentException e) {
122            log.trace(e.getMessage());
123            return false;
124        }
125        return true;
126    }
127
128    /**
129     * The default factory considers that a {@link FileSystemItem} with the given id exists if the backing
130     * {@link DocumentModel} can be fetched and {@link #isFileSystemItem(DocumentModel)} returns true.
131     *
132     * @see #isFileSystemItem(DocumentModel)
133     */
134    @Override
135    public boolean exists(String id, Principal principal) {
136        String[] idFragments = parseFileSystemId(id);
137        String repositoryName = idFragments[1];
138        String docId = idFragments[2];
139        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
140            DocumentModel doc = getDocumentById(docId, session);
141            return isFileSystemItem(doc);
142        } catch (DocumentNotFoundException e) {
143            if (log.isDebugEnabled()) {
144                log.debug(String.format("No doc related to id %s, returning false.", docId));
145            }
146            return false;
147        } catch (DocumentSecurityException e) {
148            if (log.isDebugEnabled()) {
149                log.debug(String.format("User %s cannot access doc %s, returning false.", principal.getName(), docId));
150            }
151            return false;
152        }
153    }
154
155    @Override
156    public FileSystemItem getFileSystemItemById(String id, Principal principal) {
157        String[] idFragments = parseFileSystemId(id);
158        String repositoryName = idFragments[1];
159        String docId = idFragments[2];
160        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
161            DocumentModel doc = getDocumentById(docId, session);
162            return getFileSystemItem(doc);
163        } catch (DocumentNotFoundException e) {
164            if (log.isDebugEnabled()) {
165                log.debug(String.format("No doc related to id %s, returning null.", docId));
166            }
167            return null;
168        } catch (DocumentSecurityException e) {
169            if (log.isDebugEnabled()) {
170                log.debug(String.format("User %s cannot access doc %s, returning null.", principal.getName(), docId));
171            }
172            return null;
173        }
174    }
175
176    @Override
177    public FileSystemItem getFileSystemItemById(String id, String parentId, Principal principal) {
178        String[] idFragments = parseFileSystemId(id);
179        String repositoryName = idFragments[1];
180        String docId = idFragments[2];
181        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
182            FileSystemItem parentItem = Framework.getService(FileSystemItemAdapterService.class).getFileSystemItemFactoryForId(
183                    parentId).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            if (log.isDebugEnabled()) {
191                log.debug(String.format("No doc related to id %s, returning null.", docId));
192            }
193            return null;
194        } catch (DocumentSecurityException e) {
195            if (log.isDebugEnabled()) {
196                log.debug(String.format("User %s cannot access doc %s, returning null.", principal.getName(), docId));
197            }
198            return null;
199        }
200    }
201
202    @Deprecated
203    @Override
204    public DocumentModel getDocumentByFileSystemId(String id, Principal principal) {
205        // Parse id, expecting
206        // pattern:fileSystemItemFactoryName#repositoryName#docId
207        String[] idFragments = parseFileSystemId(id);
208        String repositoryName = idFragments[1];
209        String docId = idFragments[2];
210        CoreSession session = Framework.getLocalService(FileSystemItemManager.class).getSession(repositoryName,
211                principal);
212        return getDocumentById(docId, session);
213    }
214
215    /*--------------------------- Protected ---------------------------------*/
216    protected FileSystemItem adaptDocument(DocumentModel doc, boolean forceParentItem, FolderItem parentItem) {
217        return adaptDocument(doc, forceParentItem, parentItem, false);
218    }
219
220    protected FileSystemItem getFileSystemItem(DocumentModel doc, boolean forceParentItem, FolderItem parentItem,
221            boolean includeDeleted, boolean relaxSyncRootConstraint) {
222
223        // If the doc is not adaptable as a FileSystemItem return null
224        if (!isFileSystemItem(doc, includeDeleted, relaxSyncRootConstraint)) {
225            if (log.isTraceEnabled()) {
226                log.trace(String.format("Document %s cannot be adapted as a FileSystemItem => returning null.",
227                        doc.getId()));
228            }
229            return null;
230        }
231        return adaptDocument(doc, forceParentItem, parentItem, relaxSyncRootConstraint);
232    }
233
234    protected String[] parseFileSystemId(String id) {
235
236        // Parse id, expecting pattern:
237        // fileSystemItemFactoryName#repositoryName#docId
238        String[] idFragments = id.split(AbstractFileSystemItem.FILE_SYSTEM_ITEM_ID_SEPARATOR);
239        if (idFragments.length != 3) {
240            throw new IllegalArgumentException(
241                    String.format(
242                            "FileSystemItem id %s cannot be handled by factory named %s. Should match the 'fileSystemItemFactoryName#repositoryName#docId' pattern.",
243                            id, name));
244        }
245
246        // Check if factory name matches
247        String factoryName = idFragments[0];
248        if (!name.equals(factoryName)) {
249            throw new IllegalArgumentException(String.format(
250                    "Factoy name [%s] parsed from id %s does not match the actual factory name [%s].", factoryName, id,
251                    name));
252        }
253        return idFragments;
254    }
255
256    protected DocumentModel getDocumentById(String docId, CoreSession session) {
257        return session.getDocument(new IdRef(docId));
258    }
259
260}