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 javax.jcr.Node; 022import javax.jcr.RepositoryException; 023import javax.jcr.nodetype.ConstraintViolationException; 024 025import org.apache.chemistry.opencmis.client.api.CmisObject; 026import org.apache.chemistry.opencmis.client.api.Document; 027import org.apache.chemistry.opencmis.client.api.Folder; 028import org.apache.chemistry.opencmis.client.api.ItemIterable; 029import org.apache.chemistry.opencmis.client.api.Session; 030import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; 031import org.apache.commons.lang.StringUtils; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034 035import org.ametys.plugins.explorer.ExplorerNode; 036import org.ametys.plugins.explorer.resources.ResourceCollection; 037import org.ametys.plugins.repository.AbstractAmetysObject; 038import org.ametys.plugins.repository.AmetysObject; 039import org.ametys.plugins.repository.AmetysRepositoryException; 040import org.ametys.plugins.repository.CollectionIterable; 041import org.ametys.plugins.repository.RepositoryIntegrityViolationException; 042import org.ametys.plugins.repository.UnknownAmetysObjectException; 043import org.ametys.plugins.repository.data.ametysobject.ModifiableModelLessDataAwareAmetysObject; 044import org.ametys.plugins.repository.data.holder.ModifiableModelLessDataHolder; 045import org.ametys.plugins.repository.data.holder.impl.DefaultModifiableModelLessDataHolder; 046import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 047import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData; 048import org.ametys.plugins.repository.jcr.JCRAmetysObject; 049import org.ametys.plugins.repository.jcr.SimpleAmetysObjectFactory; 050import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata; 051import org.ametys.plugins.repository.metadata.jcr.JCRCompositeMetadata; 052 053/** 054 * {@link AmetysObject} implementing the root of {@link CMISResourcesCollection}s 055 */ 056public class CMISRootResourcesCollection extends AbstractAmetysObject implements JCRAmetysObject, ResourceCollection, ModifiableModelLessDataAwareAmetysObject 057{ 058 /** application id for resources collections. */ 059 public static final String APPLICATION_ID = "Ametys.plugins.explorer.applications.resources.Resources"; 060 061 /** Metadata for repository id */ 062 public static final String DATA_REPOSITORY_ID = "repositoryId"; 063 /** Metadata for repository url */ 064 public static final String DATA_REPOSITORY_URL = "repositoryUrl"; 065 /** Attribute for repository root path */ 066 public static final String DATA_MOUNT_POINT = "mountPoint"; 067 /** Metadata for user login */ 068 public static final String DATA_USER = "user"; 069 /** Metadata for user password */ 070 public static final String DATA_PASSWORD = "password"; 071 072 private static final Logger __LOGGER = LoggerFactory.getLogger(CMISResourcesCollection.class); 073 074 /** The corresponding {@link SimpleAmetysObjectFactory} */ 075 private final CMISTreeFactory _factory; 076 077 private final Node _node; 078 079 private String _name; 080 private String _parentPath; 081 082 private Session _session; 083 private Folder _root; 084 085 /** 086 * Creates a {@link CMISRootResourcesCollection}. 087 * @param node the node backing this {@link AmetysObject} 088 * @param parentPath the parentPath in the Ametys hierarchy 089 * @param factory the CMISRootResourcesCollectionFactory which created this AmetysObject 090 */ 091 public CMISRootResourcesCollection(Node node, String parentPath, CMISTreeFactory factory) 092 { 093 _node = node; 094 _parentPath = parentPath; 095 _factory = factory; 096 097 try 098 { 099 _name = _node.getName(); 100 } 101 catch (RepositoryException e) 102 { 103 throw new AmetysRepositoryException("Unable to get node name", e); 104 } 105 } 106 107 void connect(Session session, Folder root) 108 { 109 _session = session; 110 _root = root; 111 } 112 113 Session getSession() 114 { 115 return _session; 116 } 117 118 Folder getRootFolder() 119 { 120 return _root; 121 } 122 123 @SuppressWarnings("unchecked") 124 @Override 125 public AmetysObject getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException 126 { 127 // Test if path begins with "/" and does not ends with "/" (Chemistry restriction) 128 String childPath = path; 129 if (!path.startsWith("/")) 130 { 131 childPath = "/" + path; 132 } 133 if (path.endsWith("/") && path.length() != 1) 134 { 135 childPath = path.substring(0, path.length() - 1); 136 } 137 138 if (_session == null) 139 { 140 throw new UnknownAmetysObjectException("Failed to connect to CMIS server"); 141 } 142 143 CmisObject entry = _session.getObjectByPath(childPath); 144 CmisObject object = _session.getObject(entry); 145 146 BaseTypeId baseTypeId = object.getBaseType().getBaseTypeId(); 147 148 if (baseTypeId.equals(BaseTypeId.CMIS_FOLDER)) 149 { 150 return new CMISResourcesCollection((Folder) object, this, this); 151 } 152 else if (baseTypeId.equals(BaseTypeId.CMIS_DOCUMENT)) 153 { 154 return new CMISResource((Document) object, this, this); 155 } 156 else 157 { 158 throw new UnknownAmetysObjectException("Unhandled CMIS type '" + baseTypeId + "', cannot get child at path " + path); 159 } 160 } 161 162 @Override 163 public CollectionIterable<AmetysObject> getChildren() throws AmetysRepositoryException 164 { 165 Collection<AmetysObject> aoChildren = new ArrayList<>(); 166 167 if (_session == null) 168 { 169 return new CollectionIterable<>(aoChildren); 170 } 171 172 ItemIterable<CmisObject> children = _root.getChildren(); 173 174 for (CmisObject child : children) 175 { 176 BaseTypeId typeId = child.getBaseTypeId(); 177 178 if (typeId.equals(BaseTypeId.CMIS_FOLDER)) 179 { 180 aoChildren.add(new CMISResourcesCollection((Folder) child, this, this)); 181 } 182 else if (typeId.equals(BaseTypeId.CMIS_DOCUMENT)) 183 { 184 Document cmisDoc = (Document) child; 185 186 // Check if CMIS document has content, if not ignore it 187 if (StringUtils.isNotEmpty(cmisDoc.getContentStreamFileName())) 188 { 189 aoChildren.add(new CMISResource(cmisDoc, this, this)); 190 } 191 } 192 else 193 { 194 __LOGGER.warn("Unhandled CMIS type {}. It will be ignored.", typeId); 195 } 196 } 197 198 return new CollectionIterable<>(aoChildren); 199 } 200 201 @Override 202 public boolean hasChild(String name) throws AmetysRepositoryException 203 { 204 if (_session == null) 205 { 206 return false; 207 } 208 209 ItemIterable<CmisObject> children = _root.getChildren(); 210 for (CmisObject child : children) 211 { 212 if (child.getName().equals(name)) 213 { 214 return true; 215 } 216 } 217 218 return false; 219 } 220 221 public ModifiableModelLessDataHolder getDataHolder() 222 { 223 ModifiableRepositoryData repositoryData = new JCRRepositoryData(getNode()); 224 return new DefaultModifiableModelLessDataHolder(_factory.getDataTypesExtensionPoint(), repositoryData); 225 } 226 227 @Override 228 public ModifiableCompositeMetadata getMetadataHolder() 229 { 230 return new JCRCompositeMetadata(getNode(), _factory._resolver); 231 } 232 233 @Override 234 public String getIconCls() 235 { 236 if (_session == null) 237 { 238 return "ametysicon-share40 decorator-ametysicon-sign-caution a-tree-decorator-error-color"; 239 } 240 241 String productName = _session.getRepositoryInfo().getProductName(); 242 if (productName.toLowerCase().indexOf("alfresco") != -1) 243 { 244 // FIXME EXPLORER-494 Use a dedicated Alfresco glyph 245 return "ametysicon-share40"; 246 } 247 else if (productName.toLowerCase().indexOf("nuxeo") != -1) 248 { 249 // FIXME EXPLORER-494 Use a dedicated Nuxeo glyph 250 return "ametysicon-share40"; 251 } 252 253 // FIXME EXPLORER-494 Use a dedicated CMIS glyph 254 return "ametysicon-share40"; 255 } 256 257 @Override 258 public String getApplicationId() 259 { 260 return APPLICATION_ID; 261 } 262 263 @Override 264 public String getName() throws AmetysRepositoryException 265 { 266 return _name; 267 } 268 269 @Override 270 public String getParentPath() throws AmetysRepositoryException 271 { 272 if (_parentPath == null) 273 { 274 _parentPath = getParent().getPath(); 275 } 276 277 return _parentPath; 278 } 279 280 @Override 281 public String getPath() throws AmetysRepositoryException 282 { 283 return getParentPath() + "/" + getName(); 284 } 285 286 @Override 287 public Node getNode() 288 { 289 return _node; 290 } 291 292 @Override 293 public String getId() 294 { 295 try 296 { 297 return _factory.getScheme() + "://" + _node.getIdentifier(); 298 } 299 catch (RepositoryException e) 300 { 301 throw new AmetysRepositoryException("Unable to get node UUID", e); 302 } 303 } 304 305 public boolean hasChildResources() throws AmetysRepositoryException 306 { 307 // we don't actually know if there are children or not, 308 // but it's an optimization to don't make another CMIS request 309 return true; 310 } 311 312 public boolean hasChildExplorerNodes() throws AmetysRepositoryException 313 { 314 // we don't actually know if there are children or not, 315 // but it's an optimization to don't make another CMIS request 316 return true; 317 } 318 319 @Override 320 public void rename(String newName) throws AmetysRepositoryException 321 { 322 try 323 { 324 getNode().getSession().move(getNode().getPath(), getNode().getParent().getPath() + "/" + newName); 325 } 326 catch (RepositoryException e) 327 { 328 throw new AmetysRepositoryException(e); 329 } 330 } 331 332 @Override 333 public void remove() throws AmetysRepositoryException, RepositoryIntegrityViolationException 334 { 335 try 336 { 337 getNode().remove(); 338 } 339 catch (ConstraintViolationException e) 340 { 341 throw new RepositoryIntegrityViolationException(e); 342 } 343 catch (RepositoryException e) 344 { 345 throw new AmetysRepositoryException(e); 346 } 347 } 348 349 @SuppressWarnings("unchecked") 350 @Override 351 public <A extends AmetysObject> A getParent() throws AmetysRepositoryException 352 { 353 return (A) _factory.getParent(this); 354 } 355 356 @Override 357 public void saveChanges() throws AmetysRepositoryException 358 { 359 try 360 { 361 getNode().getSession().save(); 362 } 363 catch (javax.jcr.RepositoryException e) 364 { 365 throw new AmetysRepositoryException("Unable to save changes", e); 366 } 367 } 368 369 @Override 370 public void revertChanges() throws AmetysRepositoryException 371 { 372 try 373 { 374 getNode().refresh(false); 375 } 376 catch (javax.jcr.RepositoryException e) 377 { 378 throw new AmetysRepositoryException("Unable to revert changes.", e); 379 } 380 } 381 382 @Override 383 public boolean needsSave() throws AmetysRepositoryException 384 { 385 try 386 { 387 return _node.getSession().hasPendingChanges(); 388 } 389 catch (RepositoryException e) 390 { 391 throw new AmetysRepositoryException(e); 392 } 393 } 394 395 @Override 396 public String getResourcePath() throws AmetysRepositoryException 397 { 398 return getExplorerPath(); 399 } 400 401 @Override 402 public String getExplorerPath() 403 { 404 AmetysObject parent = getParent(); 405 406 if (parent instanceof ExplorerNode) 407 { 408 return ((ExplorerNode) parent).getExplorerPath() + "/" + getName(); 409 } 410 else 411 { 412 return ""; 413 } 414 } 415 416 /** 417 * Get the user to connect to CMIS repository 418 * @return the user login 419 * @throws AmetysRepositoryException if an error occurred 420 */ 421 public String getUser() throws AmetysRepositoryException 422 { 423 return getValue(DATA_USER); 424 } 425 426 /** 427 * Get the password to connect to CMIS repository 428 * @return the user password 429 * @throws AmetysRepositoryException if an error occurred 430 */ 431 public String getPassword() throws AmetysRepositoryException 432 { 433 return getValue(DATA_PASSWORD); 434 } 435 436 /** 437 * Get the CMIS repository URL 438 * @return the CMIS repository URL 439 * @throws AmetysRepositoryException if an error occurred 440 */ 441 public String getRepositoryUrl() throws AmetysRepositoryException 442 { 443 return getValue(DATA_REPOSITORY_URL); 444 } 445 446 /** 447 * Get the CMIS repository id 448 * @return the CMIS repository id 449 * @throws AmetysRepositoryException if an error occurred 450 */ 451 public String getRepositoryId () throws AmetysRepositoryException 452 { 453 return getValue(DATA_REPOSITORY_ID); 454 } 455 456 /** 457 * Get the CMIS mount point 458 * @return the mount point 459 * @throws AmetysRepositoryException if an error occurred 460 */ 461 public String getMountPoint() throws AmetysRepositoryException 462 { 463 return getValue(DATA_MOUNT_POINT); 464 } 465 466 /** 467 * Set the URL of the CMIS repository 468 * @param url the CMIS repository URL 469 * @throws AmetysRepositoryException if an error occurred 470 */ 471 public void setRepositoryUrl(String url) throws AmetysRepositoryException 472 { 473 setValue(DATA_REPOSITORY_URL, url); 474 } 475 476 /** 477 * Set the id of the CMIS repository 478 * @param id the CMIS repository id 479 * @throws AmetysRepositoryException if an error occurred 480 */ 481 public void setRepositoryId(String id) throws AmetysRepositoryException 482 { 483 setValue(DATA_REPOSITORY_ID, id); 484 } 485 486 /** 487 * Set a mount point for the CMIS Repository 488 * @param mountPoint the mount point path 489 */ 490 public void setMountPoint(String mountPoint) 491 { 492 setValue(DATA_MOUNT_POINT, mountPoint); 493 } 494 495 /** 496 * Set a user name for the CMIS Repository 497 * @param user the login 498 */ 499 public void setUser(String user) 500 { 501 setValue(DATA_USER, user); 502 } 503 504 /** 505 * Set a password for the CMIS Repository 506 * @param password the password 507 */ 508 public void setPassword(String password) 509 { 510 setValue(DATA_PASSWORD, password); 511 } 512 513 @Override 514 public String getDescription() 515 { 516 return null; 517 } 518}