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