/* * (C) Copyright 2007-2011 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Nuxeo - initial API and implementation * * $Id: NuxeoRemotingBean.java 13219 2007-03-03 18:43:31Z bstefanescu $ */ package org.nuxeo.ecm.platform.ws; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; import javax.jws.soap.SOAPBinding.Style; import org.apache.commons.codec.binary.Base64; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.core.api.Blob; import org.nuxeo.ecm.core.api.Blobs; import org.nuxeo.ecm.core.api.CoreSession; import org.nuxeo.ecm.core.api.DataModel; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentModelList; import org.nuxeo.ecm.core.api.DocumentNotFoundException; import org.nuxeo.ecm.core.api.DocumentRef; import org.nuxeo.ecm.core.api.IdRef; import org.nuxeo.ecm.core.api.NuxeoException; import org.nuxeo.ecm.core.api.NuxeoGroup; import org.nuxeo.ecm.core.api.impl.DocumentModelImpl; import org.nuxeo.ecm.core.api.security.ACE; import org.nuxeo.ecm.core.api.security.ACL; import org.nuxeo.ecm.core.api.security.ACP; import org.nuxeo.ecm.core.api.security.impl.ACLImpl; import org.nuxeo.ecm.core.io.download.DownloadService; import org.nuxeo.ecm.platform.api.ws.DocumentBlob; import org.nuxeo.ecm.platform.api.ws.DocumentDescriptor; import org.nuxeo.ecm.platform.api.ws.DocumentLoader; import org.nuxeo.ecm.platform.api.ws.DocumentProperty; import org.nuxeo.ecm.platform.api.ws.DocumentSnapshot; import org.nuxeo.ecm.platform.api.ws.NuxeoRemoting; import org.nuxeo.ecm.platform.api.ws.WsACE; import org.nuxeo.ecm.platform.api.ws.session.WSRemotingSession; import org.nuxeo.ecm.platform.mimetype.MimetypeDetectionException; import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; import org.nuxeo.runtime.api.Framework; /** * Nuxeo remoting stateful session bean. * * @author <a href="mailto:ja@nuxeo.com">Julien Anguenot</a> * @author <a href="mailto:td@nuxeo.com">Thierry Delprat</a> */ @WebService(name = "NuxeoRemotingInterface", serviceName = "NuxeoRemotingService") @SOAPBinding(style = Style.DOCUMENT) public class NuxeoRemotingBean extends AbstractNuxeoWebService implements NuxeoRemoting { private static final long serialVersionUID = 359922583442116202L; private static final Log log = LogFactory.getLog(NuxeoRemotingBean.class); @Override @WebMethod public String getRepositoryName(@WebParam(name = "sessionId") String sid) { WSRemotingSession rs = initSession(sid); return rs.getRepository(); } @Override @WebMethod public WsACE[] getDocumentACL(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid) { WSRemotingSession rs = initSession(sid); ACP acp = rs.getDocumentManager().getACP(new IdRef(uuid)); if (acp != null) { ACL acl = acp.getMergedACLs("MergedACL"); return WsACE.wrap(acl.toArray(new ACE[acl.size()])); } else { return null; } } @Override @WebMethod public DocumentSnapshot getDocumentSnapshot(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid) { return getDocumentSnapshotExt(sid, uuid, false); } @Override public DocumentSnapshot getDocumentSnapshotExt(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid, @WebParam(name = "useDownloadURL") boolean useDownloadUrl) { WSRemotingSession rs = initSession(sid); DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid)); DocumentProperty[] props = getDocumentNoBlobProperties(doc, rs); DocumentBlob[] blobs = getDocumentBlobs(doc, rs, useDownloadUrl); ACE[] resACP = null; ACP acp = doc.getACP(); if (acp != null) { ACL acl = acp.getMergedACLs("MergedACL"); resACP = acl.toArray(new ACE[acl.size()]); } DocumentSnapshot ds = new DocumentSnapshot(props, blobs, doc.getPathAsString(), WsACE.wrap(resACP)); return ds; } @Override @WebMethod public WsACE[] getDocumentLocalACL(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid) { WSRemotingSession rs = initSession(sid); ACP acp = rs.getDocumentManager().getACP(new IdRef(uuid)); if (acp != null) { ACL mergedAcl = new ACLImpl("MergedACL", true); for (ACL acl : acp.getACLs()) { if (!ACL.INHERITED_ACL.equals(acl.getName())) { mergedAcl.addAll(acl); } } return WsACE.wrap(mergedAcl.toArray(new ACE[mergedAcl.size()])); } else { return null; } } @Override public boolean hasPermission(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid, @WebParam(name = "permission") String permission) { WSRemotingSession rs = initSession(sid); CoreSession docMgr = rs.getDocumentManager(); DocumentModel doc = docMgr.getDocument(new IdRef(uuid)); if (doc == null) { throw new DocumentNotFoundException("No such document: " + uuid); } return docMgr.hasPermission(doc.getRef(), permission); } @Override @WebMethod public DocumentBlob[] getDocumentBlobs(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid) { return getDocumentBlobsExt(sid, uuid, false); } @Override public DocumentBlob[] getDocumentBlobsExt(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid, @WebParam(name = "useDownloadUrl") boolean useDownloadUrl) { WSRemotingSession rs = initSession(sid); DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid)); if (doc == null) { return null; } return getDocumentBlobs(doc, rs, useDownloadUrl); } protected DocumentBlob[] getDocumentBlobs(DocumentModel doc, WSRemotingSession rs, boolean useDownloadUrl) { List<DocumentBlob> blobs = new ArrayList<DocumentBlob>(); String[] schemas = doc.getSchemas(); for (String schema : schemas) { DataModel dm = doc.getDataModel(schema); Map<String, Object> map = dm.getMap(); for (Map.Entry<String, Object> entry : map.entrySet()) { collectBlobs(doc.getId(), schema, rs, "", map, entry.getKey(), entry.getValue(), blobs, useDownloadUrl); } } return blobs.toArray(new DocumentBlob[blobs.size()]); } @Override @WebMethod public String[] listUsers(@WebParam(name = "sessionId") String sid, @WebParam(name = "startIndex") int from, @WebParam(name = "endIndex") int to) { WSRemotingSession rs = initSession(sid); List<String> userIds = rs.getUserManager().getUserIds(); return userIds.toArray(new String[userIds.size()]); } @Override @WebMethod public String[] listGroups(@WebParam(name = "sessionId") String sid, @WebParam(name = "startIndex") int from, @WebParam(name = "endIndex") int to) { WSRemotingSession rs = initSession(sid); List<String> groupIds = rs.getUserManager().getGroupIds(); return groupIds.toArray(new String[groupIds.size()]); } @Override @WebMethod public DocumentProperty[] getDocumentProperties(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid) { WSRemotingSession rs = initSession(sid); DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid)); List<DocumentProperty> props = new ArrayList<DocumentProperty>(); if (doc != null) { String[] schemas = doc.getSchemas(); for (String schema : schemas) { DataModel dm = doc.getDataModel(schema); Map<String, Object> map = dm.getMap(); for (Map.Entry<String, Object> entry : map.entrySet()) { collectProperty("", entry.getKey(), entry.getValue(), props); } } } return props.toArray(new DocumentProperty[props.size()]); } @Override @WebMethod public DocumentProperty[] getDocumentNoBlobProperties(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid) { WSRemotingSession rs = initSession(sid); DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid)); return getDocumentNoBlobProperties(doc, rs); } protected DocumentProperty[] getDocumentNoBlobProperties(DocumentModel doc, WSRemotingSession rs) { List<DocumentProperty> props = new ArrayList<DocumentProperty>(); if (doc != null) { DocumentLoader loader = Framework.getLocalService(DocumentLoader.class); loader.fillProperties(doc, props, rs); } return props.toArray(new DocumentProperty[props.size()]); } @Override public DocumentDescriptor getCurrentVersion(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uuid) { WSRemotingSession rs = initSession(sid); DocumentModel doc = rs.getDocumentManager().getLastDocumentVersion(new IdRef(uuid)); if (doc != null) { return new DocumentDescriptor(doc, doc.getVersionLabel()); } return null; } @Override public DocumentDescriptor getSourceDocument(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uid) { WSRemotingSession rs = initSession(sid); DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uid)); String srcid = doc.getSourceId(); if (srcid != null && !srcid.equals(uid)) { doc = rs.getDocumentManager().getSourceDocument(doc.getRef()); } if (doc != null) { return new DocumentDescriptor(doc); } return null; } @Override public DocumentDescriptor[] getVersions(@WebParam(name = "sessionId") String sid, @WebParam(name = "uuid") String uid) { WSRemotingSession rs = initSession(sid); List<DocumentModel> versions = rs.getDocumentManager().getVersions(new IdRef(uid)); if (versions == null) { return null; } DocumentDescriptor[] docs = new DocumentDescriptor[versions.size()]; int i = 0; for (DocumentModel version : versions) { docs[i++] = new DocumentDescriptor(version, version.getVersionLabel()); } return null; } @Override @WebMethod public DocumentDescriptor getRootDocument(@WebParam(name = "sessionId") String sessionId) { WSRemotingSession rs = initSession(sessionId); DocumentModel doc = rs.getDocumentManager().getRootDocument(); return doc != null ? new DocumentDescriptor(doc) : null; } @Override @WebMethod public DocumentDescriptor getDocument(@WebParam(name = "sessionId") String sessionId, @WebParam(name = "uuid") String uuid) { WSRemotingSession rs = initSession(sessionId); DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid)); return doc != null ? new DocumentDescriptor(doc) : null; } @Override @WebMethod public DocumentDescriptor[] getChildren(@WebParam(name = "sessionId") String sessionId, @WebParam(name = "uuid") String uuid) { WSRemotingSession rs = initSession(sessionId); DocumentModelList docList = rs.getDocumentManager().getChildren(new IdRef(uuid)); DocumentDescriptor[] docs = new DocumentDescriptor[docList.size()]; int i = 0; for (DocumentModel doc : docList) { docs[i++] = new DocumentDescriptor(doc); } return docs; } @SuppressWarnings("unchecked") protected void collectProperty(String prefix, String name, Object value, List<DocumentProperty> props) { final String STRINGS_LIST_SEP = ";"; if (value instanceof Map) { Map<String, Object> map = (Map<String, Object>) value; prefix = prefix + name + '/'; for (Map.Entry<String, Object> entry : map.entrySet()) { collectProperty(prefix, entry.getKey(), entry.getValue(), props); } } else if (value instanceof List) { prefix = prefix + name + '/'; List<Object> list = (List<Object>) value; for (int i = 0, len = list.size(); i < len; i++) { collectProperty(prefix, String.valueOf(i), list.get(i), props); } } else { String strValue = null; if (value != null) { if (value instanceof Blob) { try { // strValue = ((Blob) value).getString(); byte[] bytes = ((Blob) value).getByteArray(); strValue = Base64.encodeBase64String(bytes); } catch (IOException e) { throw new NuxeoException("Failed to get blob property value", e); } } else if (value instanceof Calendar) { strValue = ((Calendar) value).getTime().toString(); } else if (value instanceof String[]) { for (String each : (String[]) value) { if (strValue == null) { strValue = each; } else { strValue = strValue + STRINGS_LIST_SEP + each; } } // FIXME: this condition is always false here. } else if (value instanceof List) { for (String each : (List<String>) value) { if (strValue == null) { strValue = each; } else { strValue = strValue + STRINGS_LIST_SEP + each; } } } else { strValue = value.toString(); } // TODO: use decode method from field type? } props.add(new DocumentProperty(prefix + name, strValue)); } } @SuppressWarnings("unchecked") protected void collectBlobs(String docId, String schemaName, WSRemotingSession rs, String prefix, Map<String, Object> container, String name, Object value, List<DocumentBlob> blobs, boolean useDownloadUrl) { if (value instanceof Map) { Map<String, Object> map = (Map<String, Object>) value; prefix = prefix + name + '/'; for (Map.Entry<String, Object> entry : map.entrySet()) { collectBlobs(docId, schemaName, rs, prefix, map, entry.getKey(), entry.getValue(), blobs, useDownloadUrl); } } else if (value instanceof List) { prefix = prefix + name + '/'; List<Object> list = (List<Object>) value; for (int i = 0, len = list.size(); i < len; i++) { collectBlobs(docId, schemaName, rs, prefix, container, String.valueOf(i), list.get(i), blobs, useDownloadUrl); } } else if (value instanceof Blob) { try { Blob blob = (Blob) value; String filename = (String) container.get("filename"); if (filename == null) { filename = prefix + name; } DocumentBlob db = null; if (useDownloadUrl) { String repoName = rs.getDocumentManager().getRepositoryName(); String downloadUrl = getDownloadUrl(repoName, docId, schemaName, prefix + name, filename); db = new DocumentBlob(filename, blob.getEncoding(), blob.getMimeType(), downloadUrl); } else { db = new DocumentBlob(filename, blob); } // List<String> extensions = // rs.mimeTypeReg.getExtensionsFromMimetypeName(blob.getMimeType()); // if (extensions != null) { // db.setExtensions(extensions.toArray(new // String[extensions.size()])); // } blobs.add(db); } catch (IOException e) { throw new NuxeoException("Failed to get document blob", e); } } } protected String getSchemaPrefix(String schemaName) { // XXX : no API to get the prefix from the schemaName ! return schemaName; } protected String getDownloadUrl(String repoName, String docId, String schemaName, String xPath, String fileName) { String xpath = schemaName + ':' + xPath; DownloadService downloadService = Framework.getService(DownloadService.class); return "/" + downloadService.getDownloadUrl(repoName, docId, xpath, fileName); } @Override @WebMethod public String[] getUsers(@WebParam(name = "sessionId") String sid, @WebParam(name = "parentGroup") String parentGroup) { if (parentGroup == null) { return listUsers(sid, 0, Integer.MAX_VALUE); } WSRemotingSession rs = initSession(sid); NuxeoGroup group = rs.getUserManager().getGroup(parentGroup); if (group == null) { return null; } List<String> users = group.getMemberUsers(); return users.toArray(new String[users.size()]); } @Override @WebMethod public String[] getGroups(@WebParam(name = "sessionId") String sid, @WebParam(name = "parentGroup") String parentGroup) { WSRemotingSession rs = initSession(sid); List<String> groups; if (parentGroup == null) { groups = rs.getUserManager().getTopLevelGroups(); } else { groups = rs.getUserManager().getGroupsInGroup(parentGroup); } return groups.toArray(new String[groups.size()]); } @Override @WebMethod public String getRelativePathAsString(@WebParam(name = "sessionId") String sessionId, @WebParam(name = "uuid") String uuid) { WSRemotingSession rs = initSession(sessionId); DocumentModel doc = rs.getDocumentManager().getDocument(new IdRef(uuid)); if (doc == null) { log.debug("Document not found for uuid=" + uuid); return ""; } else { return doc.getPathAsString(); } } private Map<String, Object> createDataMap(String[] propertiesArray) { Map<String, Object> map = new HashMap<String, Object>(); for (int i = 0; i < propertiesArray.length; i += 2) { String key = propertiesArray[i]; String value = propertiesArray[i + 1]; String[] path = key.split("\\."); createSubMaps(map, path, value, 0); } return map; } @SuppressWarnings("unchecked") private void createSubMaps(Map<String, Object> map, String[] path, String value, int depth) { String key = path[depth]; if (depth == path.length - 1) { map.put(key, value); } else { Map<String, Object> subMap = (Map<String, Object>) map.get(key); if (subMap == null) { subMap = new HashMap<String, Object>(); map.put(path[depth], subMap); } createSubMaps(subMap, path, value, depth + 1); } } @Override @SuppressWarnings("unchecked") @WebMethod public String uploadDocument(@WebParam(name = "sessionId") String sid, @WebParam(name = "parentUuid") String parentUUID, @WebParam(name = "type") String type, @WebParam(name = "properties") String[] properties) { // TODO Note: This method is intented to be a general method, but now it // can only be used by NuxeoCompanionForOffice // In the future, a new method (which will set the properties of a // document from a given map) will be probably // available in org.nuxeo.ecm.core.api.impl.DocumentHelper and then this // method will be made "general". WSRemotingSession rs = initSession(sid); String name = "file_" + System.currentTimeMillis(); CoreSession documentManager = rs.getDocumentManager(); DocumentRef parentRef = new IdRef(parentUUID); DocumentModel document = new DocumentModelImpl(documentManager.getDocument(parentRef).getPathAsString(), name, type); document = documentManager.createDocument(document); Map<String, Object> propertiesMap = createDataMap(properties); Map<String, Object> fileMap = (Map<String, Object>) propertiesMap.get("file"); Map<String, Object> contentMap = (Map<String, Object>) fileMap.get("content"); Map<String, Object> dublincoreMap = (Map<String, Object>) propertiesMap.get("dublincore"); document.setProperty("dublincore", "description", dublincoreMap.get("description")); document.setProperty("dublincore", "title", dublincoreMap.get("title")); String filename = (String) fileMap.get("filename"); final byte[] contentData = Base64.decodeBase64((String) contentMap.get("data")); // String contentType = (String) contentMap.get("mime-type") ; Blob blob = Blobs.createBlob(contentData); MimetypeRegistry mimeService = Framework.getService(MimetypeRegistry.class); String mimetype; try { mimetype = mimeService.getMimetypeFromFilenameAndBlobWithDefault(filename, blob, ""); } catch (MimetypeDetectionException e) { log.error(String.format("error during mimetype detection for %s: %s", filename, e.getMessage())); mimetype = ""; } String encoding = (String) contentMap.get("encoding"); blob.setEncoding(encoding); blob.setMimeType(mimetype); document.setProperty("file", "content", blob); documentManager.saveDocument(document); documentManager.save(); return ""; } }