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; 020import java.util.List; 021import java.util.Map; 022import java.util.Optional; 023 024import org.apache.chemistry.opencmis.client.api.CmisObject; 025import org.apache.chemistry.opencmis.client.api.Document; 026import org.apache.chemistry.opencmis.client.api.Folder; 027import org.apache.chemistry.opencmis.client.api.ItemIterable; 028import org.apache.chemistry.opencmis.client.api.Session; 029import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; 030import org.apache.commons.lang3.StringUtils; 031import org.apache.commons.lang3.Strings; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034import org.xml.sax.ContentHandler; 035import org.xml.sax.SAXException; 036 037import org.ametys.plugins.explorer.ExplorerNode; 038import org.ametys.plugins.explorer.resources.ResourceCollection; 039import org.ametys.plugins.repository.AmetysObject; 040import org.ametys.plugins.repository.AmetysRepositoryException; 041import org.ametys.plugins.repository.CollectionIterable; 042import org.ametys.plugins.repository.UnknownAmetysObjectException; 043import org.ametys.plugins.repository.data.UnknownDataException; 044import org.ametys.plugins.repository.data.holder.DataHolder; 045import org.ametys.plugins.repository.data.holder.ModifiableDataHolder; 046import org.ametys.plugins.repository.data.holder.group.Composite; 047import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 048import org.ametys.plugins.repository.data.type.ModelItemTypeExtensionPoint; 049import org.ametys.runtime.model.exception.BadItemTypeException; 050import org.ametys.runtime.model.exception.NotUniqueTypeException; 051import org.ametys.runtime.model.exception.UnknownTypeException; 052import org.ametys.runtime.model.type.DataContext; 053import org.ametys.runtime.model.type.ModelItemType; 054 055/** 056 * Implementation of an {@link ExplorerNode}, backed by a CMIS server.<br> 057 */ 058public class CMISResourcesCollection implements ResourceCollection 059{ 060 /** Application id for resources collections. */ 061 public static final String APPLICATION_ID = "Ametys.plugins.explorer.applications.resources.Resources"; 062 063 private static final Logger __LOGGER = LoggerFactory.getLogger(CMISResourcesCollection.class); 064 065 private final Folder _cmisFolder; 066 private final CMISRootResourcesCollection _root; 067 private AmetysObject _parent; 068 069 /** 070 * Creates a {@link CMISResourcesCollection} 071 * @param folder The CMIS folder 072 * @param root The root of the virtual tree 073 * @param parent the parent {@link AmetysObject} if known 074 */ 075 public CMISResourcesCollection(Folder folder, CMISRootResourcesCollection root, AmetysObject parent) 076 { 077 _cmisFolder = folder; 078 _root = root; 079 _parent = parent; 080 } 081 082 @Override 083 public String getApplicationId() 084 { 085 return APPLICATION_ID; 086 } 087 088 @Override 089 public String getIconCls() 090 { 091 return "ametysicon-folder249"; 092 } 093 094 @Override 095 public String getId() throws AmetysRepositoryException 096 { 097 return getCmisRoot().getId() + "/" + getCmisFolder().getId(); 098 } 099 100 @Override 101 public String getName() throws AmetysRepositoryException 102 { 103 return getCmisFolder().getName(); 104 } 105 106 /** 107 * Retrieves the {@link CMISRootResourcesCollection} 108 * @return the {@link CMISRootResourcesCollection} 109 */ 110 public CMISRootResourcesCollection getCmisRoot () 111 { 112 return _root; 113 } 114 115 /** 116 * Retrieves the {@link Folder} 117 * @return the {@link Folder} 118 */ 119 public Folder getCmisFolder () 120 { 121 return _cmisFolder; 122 } 123 124 @SuppressWarnings("unchecked") 125 @Override 126 public AmetysObject getParent() throws AmetysRepositoryException 127 { 128 if (_parent != null) 129 { 130 return _parent; 131 } 132 133 // iterate on all parents and returns the first valid parent 134 String rootPath = _root.getRootFolder().getPath(); 135 String rootId = _root.getRootFolder().getId(); 136 for (Folder parent : _cmisFolder.getParents()) 137 { 138 // check if the parent is valid by making sure that it is below the root folder 139 // This is necessary to unsure that the parent is included in the mount point 140 if (Strings.CS.contains(parent.getPath(), rootPath)) 141 { 142 if (Strings.CS.equals(parent.getId(), rootId)) 143 { 144 _parent = getCmisRoot(); 145 } 146 else 147 { 148 _parent = new CMISResourcesCollection(parent, _root, null); 149 } 150 return _parent; 151 } 152 } 153 154 throw new AmetysRepositoryException("Failed to retrieve the parent of folder " + getName() + ". This is most likely due to the fact that we couldn't find one in the CMIS mount point."); 155 } 156 157 @Override 158 public String getParentPath() throws AmetysRepositoryException 159 { 160 return getParent().getPath(); 161 } 162 163 @Override 164 public String getPath() throws AmetysRepositoryException 165 { 166 return getParentPath() + "/" + getName(); 167 } 168 169 @Override 170 @SuppressWarnings("unchecked") 171 public AmetysObject getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException 172 { 173 Session session = getCmisRoot().getSession(); 174 if (session == null) 175 { 176 throw new UnknownAmetysObjectException("Failed to connect to CMIS server"); 177 } 178 179 CmisObject child = session.getObjectByPath(path); 180 BaseTypeId baseTypeId = child.getBaseType().getBaseTypeId(); 181 182 if (baseTypeId.equals(BaseTypeId.CMIS_FOLDER)) 183 { 184 return new CMISResourcesCollection((Folder) child, _root, this); 185 } 186 if (baseTypeId.equals(BaseTypeId.CMIS_DOCUMENT)) 187 { 188 Document cmisDoc = (Document) child; 189 // Check if CMIS document has content, if not ignore it 190 if (cmisDoc.getContentStream() != null) 191 { 192 return new CMISResource((Document) child, _root, this); 193 } 194 throw new UnknownAmetysObjectException("The CMIS document " + path + " has no content"); 195 } 196 else 197 { 198 throw new UnknownAmetysObjectException("Unhandled CMIS type '" + baseTypeId + "', cannot get child at path " + path); 199 } 200 } 201 202 203 @Override 204 public CollectionIterable<AmetysObject> getChildren() throws AmetysRepositoryException 205 { 206 Collection<AmetysObject> aoChildren = new ArrayList<>(); 207 208 ItemIterable<CmisObject> children = getCmisFolder().getChildren(); 209 for (CmisObject child : children) 210 { 211 BaseTypeId baseTypeId = child.getBaseTypeId(); 212 if (baseTypeId.equals(BaseTypeId.CMIS_FOLDER)) 213 { 214 aoChildren.add(new CMISResourcesCollection((Folder) child, _root, this)); 215 } 216 else if (baseTypeId.equals(BaseTypeId.CMIS_DOCUMENT)) 217 { 218 Document cmisDoc = (Document) child; 219 220 // Check if CMIS document has content, if not ignore it 221 if (StringUtils.isNotEmpty(cmisDoc.getContentStreamFileName())) 222 { 223 aoChildren.add(new CMISResource(cmisDoc, _root, this)); 224 } 225 } 226 else 227 { 228 __LOGGER.warn("Unhandled CMIS type {}. It will be ignored.", baseTypeId); 229 } 230 } 231 232 return new CollectionIterable<>(aoChildren); 233 } 234 235 @Override 236 public boolean hasChild(String name) throws AmetysRepositoryException 237 { 238 ItemIterable<CmisObject> children = _cmisFolder.getChildren(); 239 for (CmisObject child : children) 240 { 241 if (child.getName().equals(name)) 242 { 243 return true; 244 } 245 } 246 247 return false; 248 } 249 250 public boolean hasChildResources() throws AmetysRepositoryException 251 { 252 // we don't actually know if there are children or not, 253 // but it's an optimization to don't make another CMIS request 254 return true; 255 } 256 257 public boolean hasChildExplorerNodes() throws AmetysRepositoryException 258 { 259 // we don't actually know if there are children or not, 260 // but it's an optimization to don't make another CMIS request 261 return true; 262 } 263 264 @Override 265 public String getExplorerPath() 266 { 267 AmetysObject parent = getParent(); 268 269 if (parent instanceof ExplorerNode) 270 { 271 return ((ExplorerNode) parent).getExplorerPath() + "/" + getName(); 272 } 273 else 274 { 275 return ""; 276 } 277 } 278 279 @Override 280 public String getResourcePath() throws AmetysRepositoryException 281 { 282 return getExplorerPath(); 283 } 284 285 @Override 286 public String getDescription() 287 { 288 return null; 289 } 290 291 // Data holder methods // 292 293 public Composite getComposite(String compositePath) throws IllegalArgumentException, UnknownTypeException, BadItemTypeException 294 { 295 return null; 296 } 297 298 public boolean hasValue(String dataPath) throws IllegalArgumentException 299 { 300 return false; 301 } 302 303 public boolean hasValue(String dataPath, String dataTypeId) throws IllegalArgumentException 304 { 305 return false; 306 } 307 308 public boolean hasValueOrEmpty(String dataPath) throws IllegalArgumentException 309 { 310 return false; 311 } 312 313 public Collection<String> getDataNames() 314 { 315 return List.of(); 316 } 317 318 public <T> T getValue(String dataPath) throws IllegalArgumentException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException 319 { 320 return null; 321 } 322 323 public <T> T getValueOrDefault(String dataPath, T defaultValue) throws IllegalArgumentException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException 324 { 325 return null; 326 } 327 328 public <T> T getValueOfType(String dataPath, String dataTypeId) throws IllegalArgumentException, UnknownTypeException, BadItemTypeException 329 { 330 return null; 331 } 332 333 public <T> T getValueOfTypeOrDefault(String dataPath, String dataTypeId, T defaultValue) throws IllegalArgumentException, UnknownTypeException, BadItemTypeException 334 { 335 return null; 336 } 337 338 public boolean isMultiple(String dataPath) throws IllegalArgumentException, UnknownDataException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException 339 { 340 return false; 341 } 342 343 public boolean isMultiple(String dataPath, String dataTypeId) throws IllegalArgumentException, UnknownDataException, UnknownTypeException, BadItemTypeException 344 { 345 return false; 346 } 347 348 public <X extends ModelItemType> X getType(String dataPath) throws IllegalArgumentException, UnknownDataException, UnknownTypeException, NotUniqueTypeException 349 { 350 return null; 351 } 352 353 public ModelItemTypeExtensionPoint getModelItemTypeExtensionPoint() 354 { 355 return null; 356 } 357 358 public void copyTo(ModifiableDataHolder dataHolder) throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException 359 { 360 // Do nothing 361 } 362 363 public void copyTo(ModifiableDataHolder dataHolder, DataContext context) throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException 364 { 365 // Do nothing 366 } 367 368 public void dataToSAX(ContentHandler contentHandler, DataContext context) throws SAXException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException 369 { 370 // Do nothing 371 } 372 373 public void dataToSAX(ContentHandler contentHandler, String dataPath, DataContext context) throws SAXException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException 374 { 375 // Do nothing 376 } 377 378 public Map<String, Object> dataToJSON(DataContext context) throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException 379 { 380 return null; 381 } 382 383 public Object dataToJSON(String dataPath, DataContext context) throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException 384 { 385 return null; 386 } 387 388 public boolean hasDifferences(Map<String, Object> values) throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException 389 { 390 return false; 391 } 392 393 public RepositoryData getRepositoryData() 394 { 395 return null; 396 } 397 398 public Optional<? extends DataHolder> getParentDataHolder() 399 { 400 return Optional.empty(); 401 } 402 403 public DataHolder getRootDataHolder() 404 { 405 return null; 406 } 407}