001/* 002 * Copyright 2015 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.repository.workspace; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import javax.jcr.RepositoryException; 025 026import org.apache.avalon.framework.component.Component; 027import org.apache.avalon.framework.logger.AbstractLogEnabled; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.commons.lang3.StringUtils; 032 033import org.ametys.core.ui.Callable; 034import org.ametys.plugins.repository.AmetysObject; 035import org.ametys.plugins.repository.AmetysObjectIterable; 036import org.ametys.plugins.repository.AmetysObjectResolver; 037import org.ametys.plugins.repository.AmetysRepositoryException; 038import org.ametys.plugins.repository.ModifiableAmetysObject; 039import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 040import org.ametys.plugins.repository.RemovableAmetysObject; 041import org.ametys.plugins.repository.UnknownAmetysObjectException; 042import org.ametys.plugins.repository.jcr.JCRAmetysObject; 043import org.ametys.plugins.repository.lock.LockAwareAmetysObject; 044import org.ametys.plugins.repository.lock.LockableAmetysObject; 045import org.ametys.plugins.repository.metadata.MetadataAwareAmetysObject; 046import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata; 047import org.ametys.plugins.repository.metadata.ModifiableMetadataAwareAmetysObject; 048 049/** 050 * DAO providing methods to manage {@link AmetysObject}s. 051 */ 052public class AmetysObjectDao extends AbstractLogEnabled implements Component, Serviceable 053{ 054 055 /** The AmetysObject resolver. */ 056 protected AmetysObjectResolver _resolver; 057 058 @Override 059 public void service(ServiceManager serviceManager) throws ServiceException 060 { 061 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 062 } 063 064 /** 065 * Get information on an {@link AmetysObject} from its identifier. 066 * @param id the AmetysObject identifier. 067 * @return information on the AmetysObject as a Map. 068 */ 069 @Callable(rights = "REPOSITORY_Rights_Access", context = "/repository") 070 public Map<String, Object> getAmetysObject(String id) 071 { 072 if (getLogger().isInfoEnabled()) 073 { 074 getLogger().info("Getting AmetysObject from ID '" + id + "'"); 075 } 076 077 AmetysObject obj = _resolver.resolveById(id); 078 Map<String, Object> info = new HashMap<>(); 079 putAmetysObjectInfo(info, obj); 080 081 return info; 082 } 083 084 /** 085 * Get information on an {@link AmetysObject}, found by its path. 086 * @param path the absolute AmetysObject path (starting with /ametys:root). 087 * @return information on the AmetysObject as a Map. 088 */ 089 @Callable(rights = "REPOSITORY_Rights_Access", context = "/repository") 090 public Map<String, Object> getAmetysObjectByPath(String path) 091 { 092 if (getLogger().isInfoEnabled()) 093 { 094 getLogger().info("Getting AmetysObject at path '" + path + "'"); 095 } 096 097 AmetysObject obj = _resolver.resolveByPath(path); 098 Map<String, Object> info = new HashMap<>(); 099 putAmetysObjectInfo(info, obj); 100 101 return info; 102 } 103 104 /** 105 * Get information on a {@link AmetysObject}s from their identifier. 106 * @param ids a Collection of AmetysObject identifiers. 107 * @return a Map containing the list of AmetysObject info ('objects' key) 108 * and the list of unknown identifiers ('notFound' key). 109 */ 110 @Callable(rights = "REPOSITORY_Rights_Access", context = "/repository") 111 public Map<String, Object> getAmetysObjects(Collection<String> ids) 112 { 113 List<Map<String, Object>> objects = new ArrayList<>(); 114 List<String> notFound = new ArrayList<>(); 115 116 for (String id : ids) 117 { 118 try 119 { 120 AmetysObject obj = null; 121 if ("/".equals(id)) 122 { 123 obj = _resolver.resolveByPath("/"); 124 } 125 else 126 { 127 obj = _resolver.resolveById(id); 128 } 129 130 Map<String, Object> objectInfo = new HashMap<>(); 131 putAmetysObjectInfo(objectInfo, obj); 132 objects.add(objectInfo); 133 } 134 catch (UnknownAmetysObjectException e) 135 { 136 notFound.add(id); 137 } 138 } 139 140 Map<String, Object> result = new HashMap<>(); 141 result.put("objects", objects); 142 result.put("notFound", notFound); 143 144 return result; 145 } 146 147 /** 148 * Add an AmetysObject to the repository. 149 * @param parentId the parent AmetysObject ID, must be ModifiableTraversable. 150 * @param name the name of the AmetysObject to create. 151 * @param type the AmetysObject type. 152 * @return a Map of information on the created AmetysObject. 153 */ 154 @Callable(rights = "REPOSITORY_Rights_Access", context = "/repository") 155 public Map<String, Object> addAmetysObject(String parentId, String name, String type) 156 { 157 if (getLogger().isInfoEnabled()) 158 { 159 getLogger().info("Trying to add child: '" + name + "' to the AmetysObject of id '" + parentId + "'"); 160 } 161 162 AmetysObject obj = null; 163 if (parentId.equals("/")) 164 { 165 obj = _resolver.resolveByPath("/"); 166 } 167 else 168 { 169 obj = _resolver.resolveById(parentId); 170 } 171 172 if (!(obj instanceof ModifiableTraversableAmetysObject)) 173 { 174 throw new IllegalArgumentException(); 175 } 176 177 // Create child if modifiable traversable. 178 AmetysObject child = ((ModifiableTraversableAmetysObject) obj).createChild(name, type); 179 ((ModifiableTraversableAmetysObject) obj).saveChanges(); 180 181 Map<String, Object> childInfo = new HashMap<>(); 182 putAmetysObjectInfo(childInfo, child); 183 184 return childInfo; 185 } 186 187 /** 188 * Remove an AmetysObject. 189 * @param id the identifier of the AmetysObject to remove. 190 */ 191 @Callable(rights = "REPOSITORY_Rights_Access", context = "/repository") 192 public void removeAmetysObject(String id) 193 { 194 if (getLogger().isInfoEnabled()) 195 { 196 getLogger().info("Trying to remove the AmetysObject of id '" + id + "'"); 197 } 198 199 AmetysObject obj = null; 200 201 if (id.equals("/")) 202 { 203 obj = _resolver.resolveByPath("/"); 204 } 205 else 206 { 207 obj = _resolver.resolveById(id); 208 } 209 210 if (obj instanceof RemovableAmetysObject) 211 { 212 ModifiableAmetysObject parent = obj.getParent(); 213 ((RemovableAmetysObject) obj).remove(); 214 parent.saveChanges(); 215 } 216 } 217 218 /** 219 * Remove a metadata of a Ametys object 220 * @param id the identifier of the AmetysObject 221 * @param compositePath the path of the parent metadata. Can be null or empty. 222 * @param metadataName The name of metadata to remove 223 */ 224 @Callable(rights = "REPOSITORY_Rights_Access", context = "/repository") 225 public void removeMetadata (String id, String compositePath, String metadataName) 226 { 227 AmetysObject obj = null; 228 if (id.equals("/")) 229 { 230 obj = _resolver.resolveByPath("/"); 231 } 232 else 233 { 234 obj = _resolver.resolveById(id); 235 } 236 237 if (obj instanceof ModifiableMetadataAwareAmetysObject) 238 { 239 ModifiableCompositeMetadata holder = ((ModifiableMetadataAwareAmetysObject) obj).getMetadataHolder(); 240 241 if (StringUtils.isNotEmpty(compositePath)) 242 { 243 // deep search in composite metadata 244 if (!compositePath.isEmpty()) 245 { 246 String[] tokens = compositePath.split("/"); 247 for (int i = 0; i < tokens.length; i++) 248 { 249 holder = holder.getCompositeMetadata(tokens[i]); 250 } 251 } 252 } 253 254 holder.removeMetadata(metadataName); 255 ((ModifiableMetadataAwareAmetysObject) obj).saveChanges(); 256 } 257 } 258 259 /** 260 * Unlock an AmetysObject. 261 * @param id the identifier of the AmetysObject to remove. 262 */ 263 @Callable(rights = "REPOSITORY_Rights_Access", context = "/repository") 264 public void unlockAmetysObject(String id) 265 { 266 if (getLogger().isInfoEnabled()) 267 { 268 getLogger().info("Trying to unlock the AmetysObject of id '" + id + "'"); 269 } 270 271 AmetysObject obj = null; 272 273 if (id.equals("/")) 274 { 275 obj = _resolver.resolveByPath("/"); 276 } 277 else 278 { 279 obj = _resolver.resolveById(id); 280 } 281 282 if (obj instanceof LockableAmetysObject) 283 { 284 LockableAmetysObject lockableObject = (LockableAmetysObject) obj; 285 if (lockableObject.isLocked()) 286 { 287 lockableObject.unlock(); 288 } 289 } 290 } 291 292 /** 293 * Execute a query to find AmetysObjects. 294 * @param query the query to execute. 295 * @return a List of AmetysObject info (as Maps). 296 */ 297 @Callable(rights = "REPOSITORY_Rights_Access", context = "/repository") 298 public List<Map<String, Object>> query(String query) 299 { 300 if (getLogger().isInfoEnabled()) 301 { 302 getLogger().info("Executing logic query: " + query); 303 } 304 305 List<Map<String, Object>> results = new ArrayList<>(); 306 307 if (StringUtils.isNotBlank(query)) 308 { 309 AmetysObjectIterable<AmetysObject> objects = _resolver.query(query); 310 for (AmetysObject object : objects) 311 { 312 Map<String, Object> result = new HashMap<>(); 313 result.put("id", object.getId()); 314 result.put("name", object.getName()); 315 result.put("path", object.getPath()); 316 317 results.add(result); 318 } 319 } 320 321 return results; 322 } 323 324 /** 325 * Fill the given Map with information on an AmetysObject. 326 * @param info the Map to fill. 327 * @param obj the AmetysObject. 328 */ 329 protected void putAmetysObjectInfo(Map<String, Object> info, AmetysObject obj) 330 { 331 try 332 { 333 info.put("id", obj.getId()); 334 info.put("name", obj.getName()); 335 info.put("path", obj.getPath()); 336 337 info.put("metadataAware", obj instanceof MetadataAwareAmetysObject); 338 info.put("modifiable", obj instanceof ModifiableAmetysObject); 339 info.put("modifiableTraversable", obj instanceof ModifiableTraversableAmetysObject); 340 341 if (obj instanceof JCRAmetysObject) 342 { 343 info.put("jcrAo", true); 344 info.put("jcrPath", ((JCRAmetysObject) obj).getNode().getPath()); 345 } 346 347 if (obj instanceof LockAwareAmetysObject) 348 { 349 info.put("locked", ((LockAwareAmetysObject) obj).isLocked()); 350 info.put("lockable", obj instanceof LockableAmetysObject); 351 } 352 else 353 { 354 info.put("lockable", false); 355 } 356 } 357 catch (RepositoryException e) 358 { 359 throw new AmetysRepositoryException("Error getting information on the object " + obj.toString(), e); 360 } 361 } 362 363}