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