001/*
002 *  Copyright 2010 Anyware Services
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 */
016package org.ametys.plugins.explorer.cmis;
017
018import java.util.ArrayList;
019import java.util.Collection;
020
021import org.apache.chemistry.opencmis.client.api.CmisObject;
022import org.apache.chemistry.opencmis.client.api.Document;
023import org.apache.chemistry.opencmis.client.api.Folder;
024import org.apache.chemistry.opencmis.client.api.ItemIterable;
025import org.apache.chemistry.opencmis.client.api.Session;
026import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
027import org.apache.commons.lang.StringUtils;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031import org.ametys.plugins.explorer.ExplorerNode;
032import org.ametys.plugins.explorer.resources.ResourceCollection;
033import org.ametys.plugins.repository.AmetysObject;
034import org.ametys.plugins.repository.AmetysRepositoryException;
035import org.ametys.plugins.repository.CollectionIterable;
036import org.ametys.plugins.repository.UnknownAmetysObjectException;
037
038/**
039 * Implementation of an {@link ExplorerNode}, backed by a CMIS server.<br>
040 */
041public class CMISResourcesCollection implements ResourceCollection
042{
043    /** Application id for resources collections. */
044    public static final String APPLICATION_ID = "Ametys.plugins.explorer.applications.resources.Resources";
045
046    private static final Logger __LOGGER = LoggerFactory.getLogger(CMISResourcesCollection.class);
047    
048    private final Folder _cmisFolder;
049    private final CMISRootResourcesCollection _root;
050    private AmetysObject _parent;
051    
052    /**
053     * Creates a {@link CMISResourcesCollection} 
054     * @param folder The CMIS folder
055     * @param root The root of the virtual tree
056     * @param parent the parent {@link AmetysObject} if known
057     */
058    public CMISResourcesCollection(Folder folder, CMISRootResourcesCollection root, AmetysObject parent)
059    {
060        _cmisFolder = folder;
061        _root = root;
062        _parent = parent;
063    }
064    
065    @Override
066    public String getApplicationId()
067    {
068        return APPLICATION_ID;
069    }
070
071    @Override
072    public String getIconCls()
073    {
074        return "ametysicon-folder249";
075    }
076
077    @Override
078    public String getId() throws AmetysRepositoryException
079    {
080        return getCmisRoot().getId() + "/" + getCmisFolder().getId();
081    }
082
083    @Override
084    public String getName() throws AmetysRepositoryException
085    {
086        return getCmisFolder().getName();
087    }
088    
089    /**
090     * Retrieves the {@link CMISRootResourcesCollection}
091     * @return the {@link CMISRootResourcesCollection}
092     */
093    public CMISRootResourcesCollection getCmisRoot ()
094    {
095        return _root;
096    }
097    
098    /**
099     * Retrieves the {@link Folder}
100     * @return the {@link Folder}
101     */
102    public Folder getCmisFolder ()
103    {
104        return _cmisFolder;
105    }
106
107    @SuppressWarnings("unchecked")
108    @Override
109    public AmetysObject getParent() throws AmetysRepositoryException
110    {
111        if (_parent != null)
112        {
113            return _parent;
114        }
115        
116        String parentId = _cmisFolder.getParentId();
117        
118        // Do not rely on the root folder property of CMIS.
119        // the root can be a mount point and there for not being a root folder from the CMIS perspective
120        if (parentId.equals(_root.getRootFolder().getId()))
121        {
122            _parent = getCmisRoot();
123            return _parent;
124        }
125        else
126        {
127            Session session = getCmisRoot().getSession();
128            if (session == null)
129            {
130                throw new UnknownAmetysObjectException("Failed to connect to CMIS server");
131            }
132            
133            Folder parent = (Folder) session.getObject(parentId);
134            _parent = new CMISResourcesCollection(parent, _root, null);
135            return _parent;
136        }
137    }
138
139    @Override
140    public String getParentPath() throws AmetysRepositoryException
141    {
142        return getParent().getPath();
143    }
144
145    @Override
146    public String getPath() throws AmetysRepositoryException
147    {
148        return getParentPath() + "/" + getName();
149    }
150    
151    @Override
152    @SuppressWarnings("unchecked")
153    public AmetysObject getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException
154    {
155        Session session = getCmisRoot().getSession();
156        if (session == null)
157        {
158            throw new UnknownAmetysObjectException("Failed to connect to CMIS server");
159        }
160        
161        CmisObject child = session.getObjectByPath(path);
162        BaseTypeId baseTypeId = child.getBaseType().getBaseTypeId(); 
163        
164        if (baseTypeId.equals(BaseTypeId.CMIS_FOLDER))
165        {
166            return new CMISResourcesCollection((Folder) child, _root, this);            
167        }
168        if (baseTypeId.equals(BaseTypeId.CMIS_DOCUMENT))
169        {
170            Document cmisDoc = (Document) child;
171            // Check if CMIS document has content, if not ignore it
172            if (cmisDoc.getContentStream() != null)
173            {
174                return new CMISResource((Document) child, _root, this);  
175            }
176            throw new UnknownAmetysObjectException("The CMIS document " + path + " has no content");   
177        }
178        else
179        {
180            throw new UnknownAmetysObjectException("Unhandled CMIS type '" + baseTypeId + "', cannot get child at path " + path);
181        }
182    }
183
184    
185    @Override
186    public CollectionIterable<AmetysObject> getChildren() throws AmetysRepositoryException
187    {
188        Collection<AmetysObject> aoChildren = new ArrayList<>();
189        
190        ItemIterable<CmisObject> children = getCmisFolder().getChildren();
191        for (CmisObject child : children)
192        {
193            BaseTypeId baseTypeId = child.getBaseTypeId(); 
194            if (baseTypeId.equals(BaseTypeId.CMIS_FOLDER))
195            {
196                aoChildren.add(new CMISResourcesCollection((Folder) child, _root, this));
197            }
198            else if (baseTypeId.equals(BaseTypeId.CMIS_DOCUMENT))
199            {
200                Document cmisDoc = (Document) child;
201                
202                // Check if CMIS document has content, if not ignore it
203                if (StringUtils.isNotEmpty(cmisDoc.getContentStreamFileName()))
204                {
205                    aoChildren.add(new CMISResource(cmisDoc, _root, this));
206                }
207            }
208            else
209            {
210                __LOGGER.warn("Unhandled CMIS type {}. It will be ignored.", baseTypeId);
211            }
212        }
213
214        return new CollectionIterable<>(aoChildren);
215    }
216
217    @Override
218    public boolean hasChild(String name) throws AmetysRepositoryException
219    {
220        ItemIterable<CmisObject> children = _cmisFolder.getChildren();
221        for (CmisObject child : children)
222        {
223            if (child.getName().equals(name))
224            {
225                return true;
226            }
227        }
228        
229        return false;
230    }
231    
232    public boolean hasChildResources() throws AmetysRepositoryException
233    {
234        // we don't actually know if there are children or not, 
235        // but it's an optimization to don't make another CMIS request
236        return true;
237    }
238    
239    public boolean hasChildExplorerNodes() throws AmetysRepositoryException
240    {
241        // we don't actually know if there are children or not, 
242        // but it's an optimization to don't make another CMIS request
243        return true;
244    }
245    
246    @Override
247    public String getExplorerPath()
248    {
249        AmetysObject parent = getParent();
250        
251        if (parent instanceof ExplorerNode)
252        {
253            return ((ExplorerNode) parent).getExplorerPath() + "/" + getName();
254        }
255        else
256        {
257            return "";
258        }
259    }
260    
261    @Override
262    public String getResourcePath() throws AmetysRepositoryException
263    {
264        return getExplorerPath();
265    }
266    
267    @Override
268    public String getDescription()
269    {
270        return null;
271    }
272}