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