001/* 002 * (C) Copyright 2007-2011 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nuxeo - initial API and implementation 016 * 017 * $Id: NuxeoRemotingBean.java 13219 2007-03-03 18:43:31Z bstefanescu $ 018 */ 019 020package org.nuxeo.ecm.platform.ws; 021 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.Calendar; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028 029import javax.jws.WebMethod; 030import javax.jws.WebParam; 031import javax.jws.WebService; 032import javax.jws.soap.SOAPBinding; 033import javax.jws.soap.SOAPBinding.Style; 034 035import org.apache.commons.codec.binary.Base64; 036import org.apache.commons.logging.Log; 037import org.apache.commons.logging.LogFactory; 038import org.nuxeo.ecm.core.api.Blob; 039import org.nuxeo.ecm.core.api.Blobs; 040import org.nuxeo.ecm.core.api.CoreSession; 041import org.nuxeo.ecm.core.api.DataModel; 042import org.nuxeo.ecm.core.api.DocumentModel; 043import org.nuxeo.ecm.core.api.DocumentModelList; 044import org.nuxeo.ecm.core.api.DocumentNotFoundException; 045import org.nuxeo.ecm.core.api.DocumentRef; 046import org.nuxeo.ecm.core.api.IdRef; 047import org.nuxeo.ecm.core.api.NuxeoException; 048import org.nuxeo.ecm.core.api.NuxeoGroup; 049import org.nuxeo.ecm.core.api.impl.DocumentModelImpl; 050import org.nuxeo.ecm.core.api.security.ACE; 051import org.nuxeo.ecm.core.api.security.ACL; 052import org.nuxeo.ecm.core.api.security.ACP; 053import org.nuxeo.ecm.core.api.security.impl.ACLImpl; 054import org.nuxeo.ecm.core.io.download.DownloadService; 055import org.nuxeo.ecm.platform.api.ws.DocumentBlob; 056import org.nuxeo.ecm.platform.api.ws.DocumentDescriptor; 057import org.nuxeo.ecm.platform.api.ws.DocumentLoader; 058import org.nuxeo.ecm.platform.api.ws.DocumentProperty; 059import org.nuxeo.ecm.platform.api.ws.DocumentSnapshot; 060import org.nuxeo.ecm.platform.api.ws.NuxeoRemoting; 061import org.nuxeo.ecm.platform.api.ws.WsACE; 062import org.nuxeo.ecm.platform.api.ws.session.WSRemotingSession; 063import org.nuxeo.ecm.platform.mimetype.MimetypeDetectionException; 064import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; 065import org.nuxeo.runtime.api.Framework; 066 067/** 068 * Nuxeo remoting stateful session bean. 069 * 070 * @author <a href="mailto:ja@nuxeo.com">Julien Anguenot</a> 071 * @author <a href="mailto:td@nuxeo.com">Thierry Delprat</a> 072 */ 073@WebService(name = "NuxeoRemotingInterface", serviceName = "NuxeoRemotingService") 074@SOAPBinding(style = Style.DOCUMENT) 075public class NuxeoRemotingBean extends AbstractNuxeoWebService implements NuxeoRemoting { 076 077 private static final long serialVersionUID = 359922583442116202L; 078 079 private static final Log log = LogFactory.getLog(NuxeoRemotingBean.class); 080 081 @Override 082 @WebMethod 083 public String getRepositoryName(@WebParam(name = "sessionId") String sid) { 084 WSRemotingSession rs = initSession(sid); 085 return rs.getRepository(); 086 } 087 088 @Override 089 @WebMethod 090 public WsACE[] getDocumentACL(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid) 091 { 092 WSRemotingSession rs = initSession(sid); 093 ACP acp = rs.getDocumentManager().getACP(new IdRef(uuid)); 094 if (acp != null) { 095 ACL acl = acp.getMergedACLs("MergedACL"); 096 return WsACE.wrap(acl.toArray(new ACE[acl.size()])); 097 } else { 098 return null; 099 } 100 } 101 102 @Override 103 @WebMethod 104 public DocumentSnapshot getDocumentSnapshot(@WebParam(name = "sessionId") String sid, 105 @WebParam(name = "uuid") String uuid) { 106 return getDocumentSnapshotExt(sid, uuid, false); 107 } 108 109 @Override 110 public DocumentSnapshot getDocumentSnapshotExt(@WebParam(name = "sessionId") String sid, 111 @WebParam(name = "uuid") String uuid, @WebParam(name = "useDownloadURL") boolean useDownloadUrl) 112 { 113 WSRemotingSession rs = initSession(sid); 114 DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid)); 115 116 DocumentProperty[] props = getDocumentNoBlobProperties(doc, rs); 117 DocumentBlob[] blobs = getDocumentBlobs(doc, rs, useDownloadUrl); 118 119 ACE[] resACP = null; 120 121 ACP acp = doc.getACP(); 122 if (acp != null) { 123 ACL acl = acp.getMergedACLs("MergedACL"); 124 resACP = acl.toArray(new ACE[acl.size()]); 125 } 126 DocumentSnapshot ds = new DocumentSnapshot(props, blobs, doc.getPathAsString(), WsACE.wrap(resACP)); 127 return ds; 128 } 129 130 @Override 131 @WebMethod 132 public WsACE[] getDocumentLocalACL(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid) 133 { 134 WSRemotingSession rs = initSession(sid); 135 ACP acp = rs.getDocumentManager().getACP(new IdRef(uuid)); 136 if (acp != null) { 137 ACL mergedAcl = new ACLImpl("MergedACL", true); 138 for (ACL acl : acp.getACLs()) { 139 if (!ACL.INHERITED_ACL.equals(acl.getName())) { 140 mergedAcl.addAll(acl); 141 } 142 } 143 return WsACE.wrap(mergedAcl.toArray(new ACE[mergedAcl.size()])); 144 } else { 145 return null; 146 } 147 } 148 149 @Override 150 public boolean hasPermission(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid, 151 @WebParam(name = "permission") String permission) { 152 WSRemotingSession rs = initSession(sid); 153 CoreSession docMgr = rs.getDocumentManager(); 154 DocumentModel doc = docMgr.getDocument(new IdRef(uuid)); 155 if (doc == null) { 156 throw new DocumentNotFoundException("No such document: " + uuid); 157 } 158 return docMgr.hasPermission(doc.getRef(), permission); 159 } 160 161 @Override 162 @WebMethod 163 public DocumentBlob[] getDocumentBlobs(@WebParam(name = "sessionId") String sid, 164 @WebParam(name = "uuid") String uuid) { 165 return getDocumentBlobsExt(sid, uuid, false); 166 } 167 168 @Override 169 public DocumentBlob[] getDocumentBlobsExt(@WebParam(name = "sessionId") String sid, 170 @WebParam(name = "uuid") String uuid, @WebParam(name = "useDownloadUrl") boolean useDownloadUrl) 171 { 172 WSRemotingSession rs = initSession(sid); 173 DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid)); 174 if (doc == null) { 175 return null; 176 } 177 178 return getDocumentBlobs(doc, rs, useDownloadUrl); 179 } 180 181 protected DocumentBlob[] getDocumentBlobs(DocumentModel doc, WSRemotingSession rs, boolean useDownloadUrl) 182 { 183 List<DocumentBlob> blobs = new ArrayList<DocumentBlob>(); 184 String[] schemas = doc.getSchemas(); 185 for (String schema : schemas) { 186 DataModel dm = doc.getDataModel(schema); 187 Map<String, Object> map = dm.getMap(); 188 for (Map.Entry<String, Object> entry : map.entrySet()) { 189 collectBlobs(doc.getId(), schema, rs, "", map, entry.getKey(), entry.getValue(), blobs, useDownloadUrl); 190 } 191 } 192 return blobs.toArray(new DocumentBlob[blobs.size()]); 193 } 194 195 @Override 196 @WebMethod 197 public String[] listUsers(@WebParam(name = "sessionId") String sid, @WebParam(name = "startIndex") int from, 198 @WebParam(name = "endIndex") int to) { 199 WSRemotingSession rs = initSession(sid); 200 List<String> userIds = rs.getUserManager().getUserIds(); 201 return userIds.toArray(new String[userIds.size()]); 202 } 203 204 @Override 205 @WebMethod 206 public String[] listGroups(@WebParam(name = "sessionId") String sid, @WebParam(name = "startIndex") int from, 207 @WebParam(name = "endIndex") int to) { 208 WSRemotingSession rs = initSession(sid); 209 List<String> groupIds = rs.getUserManager().getGroupIds(); 210 return groupIds.toArray(new String[groupIds.size()]); 211 } 212 213 @Override 214 @WebMethod 215 public DocumentProperty[] getDocumentProperties(@WebParam(name = "sessionId") String sid, 216 @WebParam(name = "uuid") String uuid) { 217 WSRemotingSession rs = initSession(sid); 218 219 DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid)); 220 List<DocumentProperty> props = new ArrayList<DocumentProperty>(); 221 if (doc != null) { 222 String[] schemas = doc.getSchemas(); 223 for (String schema : schemas) { 224 DataModel dm = doc.getDataModel(schema); 225 Map<String, Object> map = dm.getMap(); 226 for (Map.Entry<String, Object> entry : map.entrySet()) { 227 collectProperty("", entry.getKey(), entry.getValue(), props); 228 } 229 } 230 } 231 return props.toArray(new DocumentProperty[props.size()]); 232 } 233 234 @Override 235 @WebMethod 236 public DocumentProperty[] getDocumentNoBlobProperties(@WebParam(name = "sessionId") String sid, 237 @WebParam(name = "uuid") String uuid) { 238 WSRemotingSession rs = initSession(sid); 239 240 DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid)); 241 return getDocumentNoBlobProperties(doc, rs); 242 } 243 244 protected DocumentProperty[] getDocumentNoBlobProperties(DocumentModel doc, WSRemotingSession rs) 245 { 246 247 List<DocumentProperty> props = new ArrayList<DocumentProperty>(); 248 if (doc != null) { 249 DocumentLoader loader = Framework.getLocalService(DocumentLoader.class); 250 loader.fillProperties(doc, props, rs); 251 } 252 return props.toArray(new DocumentProperty[props.size()]); 253 } 254 255 @Override 256 public DocumentDescriptor getCurrentVersion(@WebParam(name = "sessionId") String sid, 257 @WebParam(name = "uuid") String uuid) { 258 WSRemotingSession rs = initSession(sid); 259 DocumentModel doc = rs.getDocumentManager().getLastDocumentVersion(new IdRef(uuid)); 260 if (doc != null) { 261 return new DocumentDescriptor(doc, doc.getVersionLabel()); 262 } 263 return null; 264 } 265 266 @Override 267 public DocumentDescriptor getSourceDocument(@WebParam(name = "sessionId") String sid, 268 @WebParam(name = "uuid") String uid) { 269 WSRemotingSession rs = initSession(sid); 270 DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uid)); 271 String srcid = doc.getSourceId(); 272 if (srcid != null && !srcid.equals(uid)) { 273 doc = rs.getDocumentManager().getSourceDocument(doc.getRef()); 274 } 275 if (doc != null) { 276 return new DocumentDescriptor(doc); 277 } 278 return null; 279 } 280 281 @Override 282 public DocumentDescriptor[] getVersions(@WebParam(name = "sessionId") String sid, 283 @WebParam(name = "uuid") String uid) { 284 WSRemotingSession rs = initSession(sid); 285 List<DocumentModel> versions = rs.getDocumentManager().getVersions(new IdRef(uid)); 286 if (versions == null) { 287 return null; 288 } 289 DocumentDescriptor[] docs = new DocumentDescriptor[versions.size()]; 290 int i = 0; 291 for (DocumentModel version : versions) { 292 docs[i++] = new DocumentDescriptor(version, version.getVersionLabel()); 293 } 294 return null; 295 } 296 297 @Override 298 @WebMethod 299 public DocumentDescriptor getRootDocument(@WebParam(name = "sessionId") String sessionId) { 300 WSRemotingSession rs = initSession(sessionId); 301 DocumentModel doc = rs.getDocumentManager().getRootDocument(); 302 return doc != null ? new DocumentDescriptor(doc) : null; 303 } 304 305 @Override 306 @WebMethod 307 public DocumentDescriptor getDocument(@WebParam(name = "sessionId") String sessionId, 308 @WebParam(name = "uuid") String uuid) { 309 WSRemotingSession rs = initSession(sessionId); 310 DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid)); 311 return doc != null ? new DocumentDescriptor(doc) : null; 312 } 313 314 @Override 315 @WebMethod 316 public DocumentDescriptor[] getChildren(@WebParam(name = "sessionId") String sessionId, 317 @WebParam(name = "uuid") String uuid) { 318 WSRemotingSession rs = initSession(sessionId); 319 DocumentModelList docList = rs.getDocumentManager().getChildren(new IdRef(uuid)); 320 DocumentDescriptor[] docs = new DocumentDescriptor[docList.size()]; 321 int i = 0; 322 for (DocumentModel doc : docList) { 323 docs[i++] = new DocumentDescriptor(doc); 324 } 325 return docs; 326 } 327 328 @SuppressWarnings("unchecked") 329 protected void collectProperty(String prefix, String name, Object value, List<DocumentProperty> props) 330 { 331 final String STRINGS_LIST_SEP = ";"; 332 if (value instanceof Map) { 333 Map<String, Object> map = (Map<String, Object>) value; 334 prefix = prefix + name + '/'; 335 for (Map.Entry<String, Object> entry : map.entrySet()) { 336 collectProperty(prefix, entry.getKey(), entry.getValue(), props); 337 } 338 } else if (value instanceof List) { 339 prefix = prefix + name + '/'; 340 List<Object> list = (List<Object>) value; 341 for (int i = 0, len = list.size(); i < len; i++) { 342 collectProperty(prefix, String.valueOf(i), list.get(i), props); 343 } 344 } else { 345 String strValue = null; 346 if (value != null) { 347 if (value instanceof Blob) { 348 try { 349 // strValue = ((Blob) value).getString(); 350 byte[] bytes = ((Blob) value).getByteArray(); 351 strValue = Base64.encodeBase64String(bytes); 352 } catch (IOException e) { 353 throw new NuxeoException("Failed to get blob property value", e); 354 } 355 } else if (value instanceof Calendar) { 356 strValue = ((Calendar) value).getTime().toString(); 357 } else if (value instanceof String[]) { 358 for (String each : (String[]) value) { 359 if (strValue == null) { 360 strValue = each; 361 } else { 362 strValue = strValue + STRINGS_LIST_SEP + each; 363 } 364 } 365 // FIXME: this condition is always false here. 366 } else if (value instanceof List) { 367 for (String each : (List<String>) value) { 368 if (strValue == null) { 369 strValue = each; 370 } else { 371 strValue = strValue + STRINGS_LIST_SEP + each; 372 } 373 } 374 } else { 375 strValue = value.toString(); 376 } // TODO: use decode method from field type? 377 } 378 props.add(new DocumentProperty(prefix + name, strValue)); 379 } 380 } 381 382 @SuppressWarnings("unchecked") 383 protected void collectBlobs(String docId, String schemaName, WSRemotingSession rs, String prefix, 384 Map<String, Object> container, String name, Object value, List<DocumentBlob> blobs, boolean useDownloadUrl) 385 { 386 if (value instanceof Map) { 387 Map<String, Object> map = (Map<String, Object>) value; 388 prefix = prefix + name + '/'; 389 for (Map.Entry<String, Object> entry : map.entrySet()) { 390 collectBlobs(docId, schemaName, rs, prefix, map, entry.getKey(), entry.getValue(), blobs, 391 useDownloadUrl); 392 } 393 } else if (value instanceof List) { 394 prefix = prefix + name + '/'; 395 List<Object> list = (List<Object>) value; 396 for (int i = 0, len = list.size(); i < len; i++) { 397 collectBlobs(docId, schemaName, rs, prefix, container, String.valueOf(i), list.get(i), blobs, 398 useDownloadUrl); 399 } 400 } else if (value instanceof Blob) { 401 try { 402 Blob blob = (Blob) value; 403 String filename = (String) container.get("filename"); 404 if (filename == null) { 405 filename = prefix + name; 406 } 407 408 DocumentBlob db = null; 409 if (useDownloadUrl) { 410 String repoName = rs.getDocumentManager().getRepositoryName(); 411 String downloadUrl = getDownloadUrl(repoName, docId, schemaName, prefix + name, filename); 412 db = new DocumentBlob(filename, blob.getEncoding(), blob.getMimeType(), downloadUrl); 413 } else { 414 db = new DocumentBlob(filename, blob); 415 } 416 417 // List<String> extensions = 418 // rs.mimeTypeReg.getExtensionsFromMimetypeName(blob.getMimeType()); 419 // if (extensions != null) { 420 // db.setExtensions(extensions.toArray(new 421 // String[extensions.size()])); 422 // } 423 blobs.add(db); 424 } catch (IOException e) { 425 throw new NuxeoException("Failed to get document blob", e); 426 } 427 } 428 } 429 430 protected String getSchemaPrefix(String schemaName) { 431 // XXX : no API to get the prefix from the schemaName ! 432 return schemaName; 433 } 434 435 protected String getDownloadUrl(String repoName, String docId, String schemaName, String xPath, String fileName) { 436 String xpath = schemaName + ':' + xPath; 437 DownloadService downloadService = Framework.getService(DownloadService.class); 438 return "/" + downloadService.getDownloadUrl(repoName, docId, xpath, fileName); 439 } 440 441 @Override 442 @WebMethod 443 public String[] getUsers(@WebParam(name = "sessionId") String sid, 444 @WebParam(name = "parentGroup") String parentGroup) { 445 if (parentGroup == null) { 446 return listUsers(sid, 0, Integer.MAX_VALUE); 447 } 448 WSRemotingSession rs = initSession(sid); 449 NuxeoGroup group = rs.getUserManager().getGroup(parentGroup); 450 if (group == null) { 451 return null; 452 } 453 List<String> users = group.getMemberUsers(); 454 return users.toArray(new String[users.size()]); 455 } 456 457 @Override 458 @WebMethod 459 public String[] getGroups(@WebParam(name = "sessionId") String sid, 460 @WebParam(name = "parentGroup") String parentGroup) { 461 WSRemotingSession rs = initSession(sid); 462 463 List<String> groups; 464 if (parentGroup == null) { 465 groups = rs.getUserManager().getTopLevelGroups(); 466 } else { 467 groups = rs.getUserManager().getGroupsInGroup(parentGroup); 468 } 469 return groups.toArray(new String[groups.size()]); 470 } 471 472 @Override 473 @WebMethod 474 public String getRelativePathAsString(@WebParam(name = "sessionId") String sessionId, 475 @WebParam(name = "uuid") String uuid) { 476 WSRemotingSession rs = initSession(sessionId); 477 DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid)); 478 if (doc == null) { 479 log.debug("Document not found for uuid=" + uuid); 480 return ""; 481 } else { 482 return doc.getPathAsString(); 483 } 484 } 485 486 private Map<String, Object> createDataMap(String[] propertiesArray) { 487 Map<String, Object> map = new HashMap<String, Object>(); 488 489 for (int i = 0; i < propertiesArray.length; i += 2) { 490 String key = propertiesArray[i]; 491 String value = propertiesArray[i + 1]; 492 String[] path = key.split("\\."); 493 494 createSubMaps(map, path, value, 0); 495 } 496 497 return map; 498 } 499 500 @SuppressWarnings("unchecked") 501 private void createSubMaps(Map<String, Object> map, String[] path, String value, int depth) { 502 String key = path[depth]; 503 504 if (depth == path.length - 1) { 505 map.put(key, value); 506 } else { 507 Map<String, Object> subMap = (Map<String, Object>) map.get(key); 508 if (subMap == null) { 509 subMap = new HashMap<String, Object>(); 510 map.put(path[depth], subMap); 511 } 512 createSubMaps(subMap, path, value, depth + 1); 513 } 514 } 515 516 @Override 517 @SuppressWarnings("unchecked") 518 @WebMethod 519 public String uploadDocument(@WebParam(name = "sessionId") String sid, 520 @WebParam(name = "parentUuid") String parentUUID, @WebParam(name = "type") String type, 521 @WebParam(name = "properties") String[] properties) { 522 // TODO Note: This method is intented to be a general method, but now it 523 // can only be used by NuxeoCompanionForOffice 524 // In the future, a new method (which will set the properties of a 525 // document from a given map) will be probably 526 // available in org.nuxeo.ecm.core.api.impl.DocumentHelper and then this 527 // method will be made "general". 528 529 WSRemotingSession rs = initSession(sid); 530 String name = "file_" + System.currentTimeMillis(); 531 CoreSession documentManager = rs.getDocumentManager(); 532 DocumentRef parentRef = new IdRef(parentUUID); 533 DocumentModel document = new DocumentModelImpl(documentManager.getDocument(parentRef).getPathAsString(), name, 534 type); 535 536 document = documentManager.createDocument(document); 537 538 Map<String, Object> propertiesMap = createDataMap(properties); 539 540 Map<String, Object> fileMap = (Map<String, Object>) propertiesMap.get("file"); 541 Map<String, Object> contentMap = (Map<String, Object>) fileMap.get("content"); 542 Map<String, Object> dublincoreMap = (Map<String, Object>) propertiesMap.get("dublincore"); 543 544 document.setProperty("dublincore", "description", dublincoreMap.get("description")); 545 document.setProperty("dublincore", "title", dublincoreMap.get("title")); 546 String filname = (String) fileMap.get("filename"); 547 document.setProperty("file", "filename", filname); 548 final byte[] contentData = Base64.decodeBase64((String) contentMap.get("data")); 549 // String contentType = (String) contentMap.get("mime-type") ; 550 Blob blob = Blobs.createBlob(contentData); 551 552 MimetypeRegistry mimeService = Framework.getService(MimetypeRegistry.class); 553 554 String mimetype; 555 try { 556 mimetype = mimeService.getMimetypeFromFilenameAndBlobWithDefault(filname, blob, ""); 557 } catch (MimetypeDetectionException e) { 558 log.error(String.format("error during mimetype detection for %s: %s", filname, e.getMessage())); 559 mimetype = ""; 560 } 561 562 String encoding = (String) contentMap.get("encoding"); 563 blob.setEncoding(encoding); 564 blob.setMimeType(mimetype); 565 document.setProperty("file", "content", blob); 566 567 documentManager.saveDocument(document); 568 documentManager.save(); 569 570 return ""; 571 } 572 573}