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.io.Serializable;
022import java.security.Principal;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import javax.naming.NamingException;
028import javax.transaction.RollbackException;
029import javax.transaction.Status;
030import javax.transaction.Synchronization;
031import javax.transaction.SystemException;
032import javax.transaction.Transaction;
033
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036import org.nuxeo.drive.adapter.FileItem;
037import org.nuxeo.drive.adapter.FileSystemItem;
038import org.nuxeo.drive.adapter.FolderItem;
039import org.nuxeo.drive.adapter.RootlessItemException;
040import org.nuxeo.drive.adapter.ScrollFileSystemItemList;
041import org.nuxeo.drive.service.FileSystemItemAdapterService;
042import org.nuxeo.drive.service.FileSystemItemManager;
043import org.nuxeo.ecm.core.api.Blob;
044import org.nuxeo.ecm.core.api.CoreInstance;
045import org.nuxeo.ecm.core.api.CoreSession;
046import org.nuxeo.ecm.core.api.NuxeoException;
047import org.nuxeo.runtime.api.Framework;
048import org.nuxeo.runtime.transaction.TransactionHelper;
049
050/**
051 * Default implementation of the {@link FileSystemItemManager}.
052 *
053 * @author Antoine Taillefer
054 */
055public class FileSystemItemManagerImpl implements FileSystemItemManager {
056
057    private static final Log log = LogFactory.getLog(FileSystemItemManagerImpl.class);
058
059    /*------------- Opened sessions against each repository ----------------*/
060    protected final ThreadLocal<Map<String, CoreSession>> openedSessions = new ThreadLocal<Map<String, CoreSession>>() {
061        @Override
062        protected Map<String, CoreSession> initialValue() {
063            return new HashMap<String, CoreSession>();
064        }
065    };
066
067    @Deprecated
068    @Override
069    public CoreSession getSession(String repositoryName, Principal principal) {
070        final String sessionKey = repositoryName + "/" + principal.getName();
071        CoreSession session = openedSessions.get().get(sessionKey);
072        if (session == null) {
073            Map<String, Serializable> context = new HashMap<String, Serializable>();
074            context.put("principal", (Serializable) principal);
075            final CoreSession newSession = CoreInstance.openCoreSession(repositoryName, principal);
076            openedSessions.get().put(sessionKey, newSession);
077            try {
078                Transaction t = TransactionHelper.lookupTransactionManager().getTransaction();
079                if (t == null) {
080                    throw new RuntimeException("FileSystemItemManagerImpl requires an active transaction.");
081                }
082                t.registerSynchronization(new SessionCloser(newSession, sessionKey));
083            } catch (SystemException | NamingException | RollbackException e) {
084                throw new NuxeoException(e);
085            }
086            session = newSession;
087        }
088        return session;
089    }
090
091    /**
092     * Closer for a {@link CoreSession} object held by {@link #openedSessions}. It is synchronized with the transaction
093     * within which the {@link CoreSession} was opened.
094     */
095    protected class SessionCloser implements Synchronization {
096
097        protected final CoreSession session;
098
099        protected final String sessionKey;
100
101        protected SessionCloser(CoreSession session, String sessionKey) {
102            this.session = session;
103            this.sessionKey = sessionKey;
104        }
105
106        @Override
107        public void beforeCompletion() {
108            session.close();
109        }
110
111        @Override
112        public void afterCompletion(int status) {
113            openedSessions.get().remove(sessionKey);
114            if (status != Status.STATUS_COMMITTED) {
115                session.close();
116            }
117        }
118    }
119
120    /*------------- Read operations ----------------*/
121    @Override
122    public List<FileSystemItem> getTopLevelChildren(Principal principal) {
123        return getTopLevelFolder(principal).getChildren();
124    }
125
126    @Override
127    public FolderItem getTopLevelFolder(Principal principal) {
128        return getFileSystemItemAdapterService().getTopLevelFolderItemFactory().getTopLevelFolderItem(principal);
129    }
130
131    @Override
132    public boolean exists(String id, Principal principal) {
133        return getFileSystemItemAdapterService().getFileSystemItemFactoryForId(id).exists(id, principal);
134    }
135
136    @Override
137    public FileSystemItem getFileSystemItemById(String id, Principal principal) {
138        try {
139            return getFileSystemItemAdapterService().getFileSystemItemFactoryForId(id).getFileSystemItemById(id,
140                    principal);
141        } catch (RootlessItemException e) {
142            if (log.isDebugEnabled()) {
143                log.debug(String.format(
144                        "RootlessItemException thrown while trying to get file system item with id %s, returning null.",
145                        id));
146            }
147            return null;
148        }
149    }
150
151    @Override
152    public FileSystemItem getFileSystemItemById(String id, String parentId, Principal principal) {
153        try {
154            return getFileSystemItemAdapterService().getFileSystemItemFactoryForId(id).getFileSystemItemById(id,
155                    parentId, principal);
156        } catch (RootlessItemException e) {
157            if (log.isDebugEnabled()) {
158                log.debug(String.format(
159                        "RootlessItemException thrown while trying to get file system item with id %s and parent id %s, returning null.",
160                        id, parentId));
161            }
162            return null;
163        }
164    }
165
166    @Override
167    public List<FileSystemItem> getChildren(String id, Principal principal) {
168        FileSystemItem fileSystemItem = getFileSystemItemById(id, principal);
169        if (fileSystemItem == null) {
170            throw new NuxeoException(String.format(
171                    "Cannot get the children of file system item with id %s because it doesn't exist.", id));
172        }
173        if (!(fileSystemItem instanceof FolderItem)) {
174            throw new NuxeoException(String.format(
175                    "Cannot get the children of file system item with id %s because it is not a folder.", id));
176        }
177        FolderItem folderItem = (FolderItem) fileSystemItem;
178        return folderItem.getChildren();
179    }
180
181    @Override
182    public ScrollFileSystemItemList scrollDescendants(String id, Principal principal, String scrollId, int batchSize,
183            long keepAlive) {
184        FileSystemItem fileSystemItem = getFileSystemItemById(id, principal);
185        if (fileSystemItem == null) {
186            throw new NuxeoException(String.format(
187                    "Cannot get the descendants of file system item with id %s because it doesn't exist.", id));
188        }
189        if (!(fileSystemItem instanceof FolderItem)) {
190            throw new NuxeoException(String.format(
191                    "Cannot get the descendants of file system item with id %s because it is not a folder.", id));
192        }
193        FolderItem folderItem = (FolderItem) fileSystemItem;
194        return folderItem.scrollDescendants(scrollId, batchSize, keepAlive);
195    }
196
197    @Override
198    public boolean canMove(String srcId, String destId, Principal principal) {
199        FileSystemItem srcFsItem = getFileSystemItemById(srcId, principal);
200        if (srcFsItem == null) {
201            return false;
202        }
203        FileSystemItem destFsItem = getFileSystemItemById(destId, principal);
204        if (!(destFsItem instanceof FolderItem)) {
205            return false;
206        }
207        return srcFsItem.canMove((FolderItem) destFsItem);
208    }
209
210    /*------------- Write operations ---------------*/
211    @Override
212    public FolderItem createFolder(String parentId, String name, Principal principal, boolean overwrite) {
213        FileSystemItem parentFsItem = getFileSystemItemById(parentId, principal);
214        if (parentFsItem == null) {
215            throw new NuxeoException(String.format(
216                    "Cannot create a folder in file system item with id %s because it doesn't exist.", parentId));
217        }
218        if (!(parentFsItem instanceof FolderItem)) {
219            throw new NuxeoException(String.format(
220                    "Cannot create a folder in file system item with id %s because it is not a folder but is: %s",
221                    parentId, parentFsItem));
222        }
223        FolderItem parentFolder = (FolderItem) parentFsItem;
224        return parentFolder.createFolder(name, overwrite);
225    }
226
227    @Override
228    public FileItem createFile(String parentId, Blob blob, Principal principal, boolean overwrite) {
229        FileSystemItem parentFsItem = getFileSystemItemById(parentId, principal);
230        if (parentFsItem == null) {
231            throw new NuxeoException(String.format(
232                    "Cannot create a file in file system item with id %s because it doesn't exist.", parentId));
233        }
234        if (!(parentFsItem instanceof FolderItem)) {
235            throw new NuxeoException(String.format(
236                    "Cannot create a file in file system item with id %s because it is not a folder but is: %s",
237                    parentId, parentFsItem));
238        }
239        FolderItem parentFolder = (FolderItem) parentFsItem;
240        return parentFolder.createFile(blob, overwrite);
241    }
242
243    @Override
244    public FileItem updateFile(String id, Blob blob, Principal principal) {
245        FileSystemItem fsItem = getFileSystemItemById(id, principal);
246        return updateFile(fsItem, blob);
247    }
248
249    @Override
250    public FileItem updateFile(String id, String parentId, Blob blob, Principal principal) {
251        FileSystemItem fsItem = getFileSystemItemById(id, parentId, principal);
252        return updateFile(fsItem, blob);
253    }
254
255    @Override
256    public void delete(String id, Principal principal) {
257        FileSystemItem fsItem = getFileSystemItemById(id, principal);
258        delete(fsItem);
259    }
260
261    @Override
262    public void delete(String id, String parentId, Principal principal) {
263        FileSystemItem fsItem = getFileSystemItemById(id, parentId, principal);
264        delete(fsItem);
265    }
266
267    @Override
268    public FileSystemItem rename(String id, String name, Principal principal) {
269        FileSystemItem fsItem = getFileSystemItemById(id, principal);
270        if (fsItem == null) {
271            throw new NuxeoException(
272                    String.format("Cannot rename file system item with id %s because it doesn't exist.", id));
273        }
274        fsItem.rename(name);
275        return fsItem;
276    }
277
278    @Override
279    public FileSystemItem move(String srcId, String destId, Principal principal) {
280        FileSystemItem srcFsItem = getFileSystemItemById(srcId, principal);
281        if (srcFsItem == null) {
282            throw new NuxeoException(
283                    String.format("Cannot move file system item with id %s because it doesn't exist.", srcId));
284        }
285        FileSystemItem destFsItem = getFileSystemItemById(destId, principal);
286        if (destFsItem == null) {
287            throw new NuxeoException(String.format(
288                    "Cannot move a file system item to file system item with id %s because it doesn't exist.", destId));
289        }
290        if (!(destFsItem instanceof FolderItem)) {
291            throw new NuxeoException(String.format(
292                    "Cannot move a file system item to file system item with id %s because it is not a folder.",
293                    destId));
294        }
295        return srcFsItem.move((FolderItem) destFsItem);
296    }
297
298    /*------------- Protected ---------------*/
299    protected FileSystemItemAdapterService getFileSystemItemAdapterService() {
300        return Framework.getLocalService(FileSystemItemAdapterService.class);
301    }
302
303    protected FileItem updateFile(FileSystemItem fsItem, Blob blob) {
304        if (fsItem == null) {
305            throw new NuxeoException("Cannot update the content of file system item because it doesn't exist.");
306        }
307        if (!(fsItem instanceof FileItem)) {
308            throw new NuxeoException(
309                    String.format("Cannot update the content of file system item with id %s because it is not a file.",
310                            fsItem.getId()));
311        }
312        FileItem file = (FileItem) fsItem;
313        file.setBlob(blob);
314        return file;
315    }
316
317    protected void delete(FileSystemItem fsItem) {
318        if (fsItem == null) {
319            throw new NuxeoException("Cannot delete file system item because it doesn't exist.");
320        }
321        fsItem.delete();
322    }
323
324}