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