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