How can we upload documents using the upload widget in Bonita BPM 7.4.2 by binding it with a DB connector?

My use case is to allow the user to upload document(s) to a MongoDB server from the UI.

I have designed the form for this purpose but I am encountering few problems in configuring the DB Connector and have little to no idea how to bind the upload widget with the DB connector. Specifically, the upload widget has a property - URL that can be configured to tell where the data needs to go; I wish to know how I can specify this property if I have a configured DB Connector.

Thanks in advance!!

I’ll be honest - just use a script connector, it’s much easier to control and use.

And you’ll know the code.

The UI will return the document as part of a contract - you then just have to access the document through the documentID from the output operation, from there it’s quite easy to build the document as a byte stream and upload it to the database through a Insert command.

regards
Seán

regards
Seán

PS: As this reply offers an answer your question, and if you like it, please Mark UP and/or as Resolved.

  • If I am facilitating the upload of the documents via a script in the operations tab; I can do all tasks such as connecting to DB and actually uploading docs through. Do I still need a connector configuration?

No not really, but one thing we found very quickly was we wanted to use it again, and again, and again, and…I think you get the picture.

It is easy to think you will do this once when in-fact we use it at least 560 times. A subroutine was the only way to go.

  • How does the information go back to the operations tab by clicking the Submit Button - does this need to be mentioned somewhere in the properties of the button?

How else would it get back? You should format the formOutput variable to send back the collection. In your table you will have ADD button to add a new document, this in-fact adds to the collection, it is the collection you will be sending back and then save to a variable which you can process via a Script Task calling the subroutine which calls the upload script.

  • How can I give positive/ negative feedback to the user if the actual upload succeeded or not?

We don’t do that, we only allow the Admin to sort that out as the file is uploaded on submit, how exactly we don’t worry about it. We trust the system in this case. :wink:

All right, thank you for this. It seems easier than trying to configure a MongoDB Connector with the UI.

Please pardon my naivety, but I just need to confirm if I am doing the right thing to facilitate the upload of documents. So, I need to have a contract for the files being uploaded as well as one for the document ID (or documentName as well, optionally). My doubt is more inclined towards knowing this:
I made a simple form where a user can upload multiple documents by clicking the add button. So, when I click the paperclip button and choose a file for upload, where does the file get stored – is it some local/temp folder on the server where the form is served? I assume, that this shall work only if I actually run the application and shall not work if I run in Preview mode(as I encountered an error stating “Upload failed” when I ran it in Preview Mode).

The script connector that you mentioned in the answer will be used to convert the file object to a byte stream and upload to the server – is this understanding correct? Can I facilitate the triggering of this script on the action of a button?

Your help and guidance is greatly appreciated!!

Thanks

the way we do it is:

Add the file widget to the page
click submit
submit sends the file information back to the operations tab
in the operations tab we take the documentID and just upload the document to the database.

As you’re doing a variable number of documents the contract will really be for the collection of documents I suppose. there is no need to give a contract for the file name this will be accessible from the documentID.

The file will as you say get stored in in /temp directory which will then be accessible through the documentID.

And yes, this will only work as part of the process - not preview.

the Script Connector - yes it will be used to upload the file to the database, and there will be no need to trigger the script by button because you’ll either have it as an operation, or a follow on Script task.

We actually created our own Connector (Development → Connector Implementation) to do this for us, all we send it is the db parameters (login etc.) and the docID.

I think you will do something like in the operation

(target) fileContainer set value as uiContainerWithFiles

Then the next task is a Script Task that multiInstantiates a subroutine that does the actual upload.

here is our PostgreSQL code :slight_smile: :

package org.xxxxrnare.connector.xxxxPutDocumentToPostgres110Impl;

import java.io.ByteArrayInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.bonitasoft.engine.api.DocumentAPI;
import org.bonitasoft.engine.api.ProcessRuntimeAPI;
import org.bonitasoft.engine.bpm.document.Document;
import org.bonitasoft.engine.bpm.document.DocumentNotFoundException;
import org.bonitasoft.engine.connector.ConnectorException;

/**
*The connector execution will follow the steps

  • 1 - setInputParameters() → the connector receives input parameters values

  • 2 - validateInputParameters() → the connector can validate input parameters values

  • 3 - connect() → the connector can establish a connection to a remote server (if necessary)

  • 4 - executeBusinessLogic() → execute the connector

  • 5 - getOutputParameters() → output are retrieved from connector

  • 6 - disconnect() → the connector can close connection to remote server (if any)
    */
    public class xxxxPutDocumentToPostgres110Impl extends
    AbstractxxxxPutDocumentToPostgres110Impl {

    Logger logger= Logger.getLogger(“org.bonitasoft”);
    String module = null;

    //inputs
    Map<String, String> dbParms = new HashMap<String, String>();
    Long docID;

    //outputs
    Boolean success;
    List listOutput = new ArrayList();

    //locals
    Boolean debug = false;

    //set basics for db
    Connection con = null;
    PreparedStatement pst = null;
    ResultSet rs = null;
    String sql2 = null;
    String dbTableDOM = null;
    String dbTableTSK = null;
    Boolean firstReturnSet = false;

    String tble = “xxxxdocuments”;

    String title = null;

    @SuppressWarnings(“unchecked”)
    @Override
    protected void executeBusinessLogic() throws ConnectorException{
    //Get access to the connector input parameters
    //getDbParms();

     getClassName();
    
     dbParms = getDbParms();
     docID = getDocID();
    
     if(debug){dumpDataInputs();}
     initializeOutputs();
    
     //TODO execute your business logic here 
    
     try {
     	Class.forName("org.postgresql.Driver");
     	String url = "jdbc:postgresql://"+dbParms.get("dbHost")+":"+dbParms.get("dbPort")+"/"+dbParms.get("dbDatabase");
     	if(debug){logger.severe(module+": url: "+url);}
     	con = DriverManager.getConnection(url, dbParms.get("dbUser"),dbParms.get("dbPassword"));
     	if(debug){logger.severe(module+": con: "+con);}
    
     	PreparedStatement ps = con.prepareStatement("INSERT INTO " + tble
     			+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
    
     	// document stuff
     	 
     	ProcessRuntimeAPI processRuntimeAPI = apiAccessor.getProcessAPI();
    
     	Document d1 = apiAccessor.getProcessAPI().getDocument(docID);
     	String xxxxdocumentname = d1.getContentFileName();
     	String mimeType = d1.getContentMimeType();
     	// use fileName if title is empty
     	title = (title==null||title.isEmpty()) ? d1.getContentFileName():title;
     	final byte[] contentByte = ((DocumentAPI) processRuntimeAPI).getDocumentContent(d1.getContentStorageId());
     	final ByteArrayInputStream xxxxdocument = new ByteArrayInputStream(contentByte);
     	int xxxxdoclength = contentByte.length;
    
     	if(debug){logger.severe(module+"@String xxxxdocumentname "+xxxxdocumentname);}
     	if(debug){logger.severe(module+"@String mimeType "+mimeType);}
     	if(debug){logger.severe(module+"@title "+title);}
     			
     	Calendar calendar = Calendar.getInstance();
     	Timestamp date = new java.sql.Timestamp(calendar.getTime().getTime());
    

// calendar.set(Calendar.YEAR, 2015);
// calendar.set(Calendar.MONTH, 1);
// calendar.set(Calendar.DAY_OF_MONTH, 1);
// Date date = new Date();

		//1 xxxxdocumentid
		//2 xxxxyyyyydocumentname
		//3 xxxxdocumentname
		//4 xxxxversionmajor
		//5 xxxxversionminor
		//6 xxxxversionmini
		//7 uploader
		//8 approver
		//9 uploaddate
		//10 approvaldate
		//11 xxxxstatus
		//12 xxxxdocument
	       
	    ps.setLong(1, 0);
		ps.setString(2, removeExtension(xxxxdocumentname.replace("_", " "))); // remove 
		ps.setString(3, xxxxdocumentname);
		ps.setLong(4, 0);
		ps.setLong(5, 0);
		ps.setLong(6, 0);
		ps.setString(7, "Uploader");
		ps.setString(8, "Approver");
		ps.setTimestamp(9, date);
		ps.setTimestamp(10, date);
		ps.setString(11, "Pending");
		ps.setBinaryStream(12, xxxxdocument, xxxxdoclength);
		ps.executeUpdate();

		ps.close();
		con.close();
		
	} catch (ClassNotFoundException e) {
		logger.info("After Load Class: ClassNotFoundException");
	} catch (SQLException eSQL) {
		// TODO Auto-generated catch block
		eSQL.printStackTrace();
	} catch (DocumentNotFoundException eDoc) {
		// TODO Auto-generated catch block
		eDoc.printStackTrace();
	}

	//WARNING : Set the output of the connector execution. If outputs are not set, connector fails
	dumpDataOutputs();
	setSuccess(success);

 }

private void getClassName(){
	Class<?> enclosingClass = getClass().getEnclosingClass();
	if (enclosingClass != null) {
		module = enclosingClass.getName();
	} else {
		module = getClass().getName();
	}
}

private void dumpDataInputs(){
	if(debug){logger.info(module+": Start");
	logger.info(module+": Starting: getDbParms: "+getDbParms());
	logger.info(module+": Starting: docID: "+docID.toString());}
}

private void dumpDataOutputs(){
	if(debug){logger.info(module+": Ending: success: "+success);
	logger.info(module+": End");}
}

private void initializeOutputs(){
	//set default outputs
	success = true;
}

private static String removeExtension(String s) {

    String separator = System.getProperty("file.separator");
    String filename;

    // Remove the path upto the filename.
    int lastSeparatorIndex = s.lastIndexOf(separator);
    if (lastSeparatorIndex == -1) {
        filename = s;
    } else {
        filename = s.substring(lastSeparatorIndex + 1);
    }

    // Remove the extension.
    int extensionIndex = filename.lastIndexOf(".");
    if (extensionIndex == -1)
        return filename;

    return filename.substring(0, extensionIndex);
}

@Override
public void connect() throws ConnectorException{
	//[Optional] Open a connection to remote server

}

@Override
public void disconnect() throws ConnectorException{
	//[Optional] Close connection to remote server

}

}

Hope it helps :slight_smile:

Thank you so much for taking the time to clarify my doubts regarding the entire process. This is really helpful
Though, I still have last couple of clarifications -

  • If I am facilitating the upload of the documents via a script in the operations tab; I can do all tasks such as connecting to DB and actually uploading docs through. Do I still need a connector configuration?

  • How does the information go back to the operations tab by clicking the Submit Button - does this need to be mentioned somewhere in the properties of the button?

  • How can I give positive/ negative feedback to the user if the actual upload succeeded or not?

Thanks a lot!

Thanks a lot, Sean! I think I am getting to understand the picture piece by piece so some rudimentary doubts keep coming up.

This is great help!!

That being said, I’ve just throw a quick something together and I’m also confused a little…

Will get back to you on this.

regards