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