Skip to Content
Technical Articles
Author's profile photo Sharadha Krishnamoorthy

SAP Cloud Platform Document Services – Upload files from Fiori application

I am back with yet another blog about one of the services in SAP Cloud Platform – ‘SAP Cloud Platform Document Service’. The document service offers persistent storage for content and provides additional functionality. It also provides a standardized interface for content using the OASIS CMIS standard.

As always, SAP has provided good amount of documentation to start exploring the service – link. The documentation covers the following two scenarios in detail. (Possible deployment scenarios are discussed here – Deployment Options)

1. Consuming the Document service from a Java application – Java application
2. Access the document service from External Applications through Proxy Bridge – Proxy Bridge

In this blog, I will explain the steps required to access  the SAP Cloud Platform Document Service from UI5/Fiori applications deployed on the SAP Cloud Platform itself.

Pre-requisites:

  1. A trial account in SAP Cloud Platform (Neo) (http://account.hanatrial.ondemand.com)
  2. Eclipse configured with SAP Cloud Platform tools for Java. Refer Set up Eclipse – This tutorial covers the steps to install SAP Cloud Platform Tools and SAP Cloud Platform SDK for Java. Please make sure that you install the SDK for Java EE 7 Web Profile TomEE 7 (from https://tools.hana.ondemand.com/#cloud ) and not the one mentioned in the tutorial.
  3. If you have not deployed a Java application to the Cloud Platform before, get yourself familiarised using the tutorial Deploying a basic Java application on SAP Cloud Platform

1. Enable the SAP Cloud Document Service in Cloud.

As with any service on SAP Cloud Platform, the first step is to enable the service in your trial subaccount.

Once you enable the service, you can manage the Document Repositories from the cockpit. Refer –managing Document Repositories in the Cockpit. We will be creating the repositories from the Java application itself for this use case.

2. Create a Java Web Service application to access the Document Service.

Create a Dynamic Web Project in Eclipse. Make sure to use the JAVA EE 7 Web as Target Runtime, The is required for JAX-RS webservices. Once the application is created, open the Web.xml and add the following resource reference.

<resource-ref>
   <res-ref-name>EcmService</res-ref-name>
   <res-type>com.sap.ecm.api.EcmService</res-type>
</resource-ref>

The document service is consumed by defining a resource in your web.xml file and by using JNDI lookup to retrieve an instance of the com.sap.ecm.api.EcmService class. Once you have established a connection to the document service, you can use one of theconnect(…) methods to get a CMIS session

Create a Java class – ManageDocuments with the following methods

  • getSession – This method retrieves the CMIS session for the document repository. It creates a repository, if it does not exists already.
private Session getSession() {
		String uniqueName = "<<Any Name>>";
		String secretKey = "<<Secret Key - provide any key>>";
		Session openCmisSession = null;
		EcmService ecmSvc = null;
		InitialContext ctx;
		try {
			ctx = new InitialContext();
			String lookupName = "java:comp/env/" + "EcmService";
			ecmSvc = (EcmService) ctx.lookup(lookupName);

		} catch (NamingException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		try {
			// connect to my repository
			openCmisSession = ecmSvc.connect(uniqueName, secretKey);
		} catch (CmisObjectNotFoundException e) {
			// repository does not exist, so try to create it
			RepositoryOptions options = new RepositoryOptions();
			options.setUniqueName(uniqueName);
			options.setRepositoryKey(secretKey);
			options.setVisibility(Visibility.PROTECTED);
			ecmSvc.createRepository(options);
			// should be created now, so connect to it
			openCmisSession = ecmSvc.connect(uniqueName, secretKey);
		}
		return openCmisSession;

	}
  • create – This method creates the folder and uploads the file using the information passed as JSON. This method access the getSession() to access the CMIS session.
// local classes
class result {
	String message;

	void setMessage(String message) {
		this.message = message;
	}
}
class uploadData{
	String file;
	String fileName;
	String folderName;
	void setFile(String file) {
		this.file = file;
	}
	void setFileName(String fileName) {
		this.fileName = fileName;
	}
	void setFolderName(String folderName) {
		this.folderName = folderName;
	}
	
}

/**
	 * Creates the folder and file
	 * @param upload
	 * @return
	 */
	@POST
	@Path("/create")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public Response create(uploadData upload) {
		result r = new result();
		String message = "";
		Session openCmisSession = getSession();
		// access the root folder of the repository
		Folder root = openCmisSession.getRootFolder();
		// create a new folder
		try {
			Map<String, String> newFolderProps = new HashMap<String, String>();
			newFolderProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder");
			newFolderProps.put(PropertyIds.NAME, upload.folderName);
			root.createFolder(newFolderProps);
			message = message + "Folder "+upload.folderName + " created in root." + "\n";
		} catch (CmisNameConstraintViolationException e) {
			// Folder exists already, nothing to do
			message = message + "Folder "+upload.folderName + " already exists." + "\n";
		}

		// create a new file in the folder
		List<Tree<FileableCmisObject>> folderTree = root.getFolderTree(1);
		Tree<FileableCmisObject> treeo = folderTree.get(0);
		Folder f = (Folder)treeo.getItem();
		Map<String, Object> properties = new HashMap<String, Object>();
		properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");
		try {
			properties.put(PropertyIds.NAME, upload.fileName);
			byte[] fileContent;
			fileContent = upload.file.getBytes("UTF-8");

			InputStream stream = new ByteArrayInputStream(fileContent);
			ContentStream contentStream = openCmisSession.getObjectFactory().createContentStream(upload.fileName,
					fileContent.length, "text/plain; charset=UTF-8", stream);

			f.createDocument(properties, contentStream, VersioningState.NONE);
			message = message + "File "+upload.fileName + " created under Folder " + f.getName() + ".\n";
		} catch (CmisNameConstraintViolationException e) {
			// Document exists already, nothing to do
			message = message + "File "+upload.fileName + " already exists in the folder "+ f.getName() + ".\n";

		} catch (UnsupportedEncodingException e1) {
			// TODO Auto-generated catch block
			r.setMessage(e1.getMessage());
			return Response.ok(r, MediaType.APPLICATION_JSON).build();
			

		} 
		r.setMessage(message);
		return Response.ok(r, MediaType.APPLICATION_JSON).build();
	}

 

3. Deploy and test the Java Web Service application on the SAP Cloud Platform.

Once the class is ready, deploy it on the SAP cloud Platform. You can view the deployed application under ‘Java applications’ in the Cloud Platform Cockpit.

You can see that there are no repositories under ‘Document repositories’ in your trial account.

Let us test the deployed application from Postman to make sure that it can access and create the documents in the repository.

The web service requires JSON data with folder name, file name and the file contents to create them in the document repository. We can test the service by supplying these in the body of the post request. Make sure you pass the content type as ‘application/json’ in the request header.

I have coded the java webservice to give a confirmation of the steps completed.

Go back to the SAP Cloud Platform Cockpit and you will see the new document repository created with the name you have given in the java application.

 

4. Create a UI5/Fiori application to access the Java Web Service application.

It is a simple sales order application using the oData services provided by SAP Gateway demo server ES5. It lists the orders and order line items in a Master-Detail application.

I have added few lines of code under the attachment tab to add the file upload functionality. Before we can start with the changes, we need a destination in SAP Cloud Platform to access the java application.

Check the destination and make sure that it is reachable.

Add the view elements in view.xml to add the File upload control to the application. Open the controller file and add the following code in the action handler function which handles the upload functionality. Basically we will be sending the current sales order number for the folder name to be created.

	onHandleUploadPress: function (oEvent) {

			var oFileUpload = this.getView().byId("fileUploader");
			var domRef = oFileUpload.getFocusDomRef();
			var file = domRef.files[0];
			var that = this;
			//Get the file name and type
			this.fileName = file.name;
			this.fileType = file.type;
			//get the order number to be used as folder name
			this.folderName = this.getModel("detailView").getData().sObjectId;
			var reader = new FileReader();
			reader.onload = function (e) {
				var content = e.currentTarget.result.replace("data:" + file.type + ";base64,", "");
				//call the method to make the ajax call to the webservice,
				that.sendFiletoCloud(that.folderName, that.fileName, content);
			};
			reader.readAsDataURL(file);

		},

The method below makes the Ajax call to the web service destination.

	sendFiletoCloud: function (folderName, fileName, file) {
			jQuery.ajax({
				url: "/clouddocumentservice/managedocuments/create",
				type: "POST",
				data: JSON.stringify({
					folderName: folderName,
					fileName: fileName,
					file: file

				}),
				dataType: "json",
				contentType: "application/json",
				success: function (data) {
					MessageToast.show("File uploaded successfully");
				},
				error: function (e) {
					MessageToast.show("Error while uploading the file");
				}
			});
		},

 

Now, let us upload a file and test.

 

We have now successfully accessed and created the documents in the repository. It is pretty easy to use CMIS API to read the contents of the repository as well. I am not covering the entire flow in this blog. I am sharing the method which I used to read the contents of the repository to check the documents uploaded.

@GET
	@Path("/read")
	public String getobjects() {
		String returnvalue = "";
		Session openCmisSession = getSession();
		returnvalue = "You are connected to the repository: " + openCmisSession.getRepositoryInfo().getId() + "\n";
		// access the root folder of the repository
		Folder root = openCmisSession.getRootFolder();
		ItemIterable<CmisObject> children = root.getChildren();
		for (CmisObject o : children) {
			if (o instanceof Folder) {
				Folder f = (Folder)o;
				returnvalue =  returnvalue + "Folder Name: " + o.getName() + "\n";
				ItemIterable<CmisObject> Fchildren = f.getChildren();
				for (CmisObject c : Fchildren) {
					if(c instanceof Document) {
						Document doc = (Document) c;
					returnvalue = returnvalue  + "File Name: " + doc.getName() + "---" + " filesize: "
							+ doc.getContentStreamLength() + " bytes"+ "\n";;
					}
				}
				
			} else {
				Document doc = (Document) o;
				returnvalue = returnvalue + " createdBy: " + o.getCreatedBy() + " filesize: "
						+ doc.getContentStreamLength() + " bytes" + "\n";;

			}
		}

		return returnvalue;

	}

We can check the document uploaded from the Ui5 application by calling the method above from Postman.

As always, please feel free to comment if you have any feedback or questions.

Assigned Tags

      6 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Nikhila Moreira
      Nikhila Moreira

      Hi,

      As you suggested we developed one webservice in java platform using jersy framework.

      In the below code

      @POST
      	@Path("/create")
      	@Consumes(MediaType.APPLICATION_JSON)
      	@Produces(MediaType.APPLICATION_JSON)
      	public Response create(uploadData upload) {
      		result r = new result();
      		String message = "";
      ----------------------------------
      -----------------------------------
      
      }
      

       

      We are clear about all the classes and function used by you on  "Create" function Except the "uploadData". is this a datatype or any class from lib?

      uploadData upload
      upload.folderName
      upload.fileName
      upload.file.getBytes("UTF-8");

       

      It seems that input data passed while triggering the webservice is received from this "uploadData" at server side.In normal webservice applications we are passing files using form submit method.While running this on java environment we got the error like uploadData not defined.

       

      result r = new result();

      Also it seems like this result is a class and using this class we are building output result which is used to send to client and we have doubt with this class definition(we cannot find this classdefinition anywhere in the above code).

       

      Please put more light on the uploadData and result used in the above webservice method.

       

      Regards,

      Nikhila

      Author's profile photo Sharadha Krishnamoorthy
      Sharadha Krishnamoorthy
      Blog Post Author

      Hi Nikhila,

       

      Both of them - uploadData and result - are local classes. I have updated the blog with the code snippet for them as well now.

      -Sharadha

      Author's profile photo Nikhila Moreira
      Nikhila Moreira

      Hi,

       

      I have completed the java web service and when I try to configure my destination I am getting a message 302:moved temporarily.

       

      This is how I have configured my destination.

       

      Also, how do you consume this webservice in sapui5 application?

      Thanks in advance,

      Regards,

      Nikhila

      Author's profile photo Sharadha Krishnamoorthy
      Sharadha Krishnamoorthy
      Blog Post Author

      Hi Nikhila,

      Please ignore the 302 error. i too got the same but the webservice will work fine.  You can find a number of blogs and documentation in SAP help which describes how to consume destinations in SAP ui5 application. i have given a tutorial which shows the same.

      https://developers.sap.com/tutorials/cp-ui5-ms-graph-create-app.html

      In order to access the destinations from SAP Web ide, you have to make sure that the following properties are maintained at the destination. you can find loads of information regarding this as well in the help documents.

       

       

       

       

       

      Author's profile photo Chris Paine
      Chris Paine

      Something that might be worth sharing - goodness know, hopefully it will help someone! - When you create a connection to an existing repository using:

      // connect to my repository
      			openCmisSession = ecmSvc.connect(uniqueName, secretKey);

      the connection/document id is tenant specific! So if you are an application that has multiple tenants subscribing to it - a servlet call that comes from subscribed tenant A will be able to open a repository created on the main instance, but will not be able to read documents using the same id as same repository opened by a servlet created from a different tenant.

      Thus, create a document with id abcd via tenant A, you will not be able to read it from tenant B...

      This proved very problematic for me!

      I had to use tenant context service to run the repository connection fetch in a known tenant - then use that repository reference for all tenants to be able to fetch the same content.

      Anyway - hope that helps someone in the future who is scratching their head and saying "But the document DOES exist, I just freaking well opened it!"

      Cheers,

      Chris

       

       

       

      Author's profile photo Daniel Kullmann
      Daniel Kullmann

      I have a question about this: I am using the document service on Neo, and it all works, except I can't use the cmis:createdBy property to store the user who has created a file. I set the value into the properties when creating the file, but it is ignored, and when I want to fetch it again I get either an empty string back (right after I have created the file) or the string "{sap:builtin}anonymous", when I query the document later.