UCCX: Read/Write from/to SFTP/FTP/?

Document

Thu, 09/11/2014 - 11:37
May 17th, 2013
User Badges:
  • Green, 3000 points or more
  • Community Spotlight Award,

    Member's Choice, December 2015



This document was inspired by this comment.


Problem: reading or writing documents (e.g. recordings) to locations accessible by either the SSH File Transfer Protocol (SFTP) or the File Transfer Protocol (FTP) may be required by an UCCX script.


Analysis: the Apache Commons Virtual File System project (VFS) is a Java library which is able to handle various types of file systems providing a single abstraction layer. With UCCX Premium or IP IVR it's possible to inject custom Java code into scripts, so provided the VFS library and its dependencies are available on the UCCX classpath, reading or writing a file using a supported protocol becomes relatively easy.


Please note:

  • SFTP is not FTP over SSH and is not FTP over SSL - the latter is also supported by VFS, it has not been tested with UCCX when creating this document.
  • This is a proof of concept, it has not been optimised or extensively tested.
  • The scripts explained here load and save the files fully before proceeding to the next step, blocking script execution. This may lead to noticeable delays. One may consider putting the caller on hold to provide some music on hold music while the input/output operations finish.
  • Due to a bug in UCCX version 7.0 it's not possible to call the toByteArray() method on an instance of a ByteArrayOutputStream - this is why I am using a ByteBuffer here, which needs to allocate a fixed length byte array. If there's a chance that the file is not closed when invoking this script, meaning a different process still writes to it, the file will be read partially.


* * * Disclaimer: the necessary blah-blah. I am not responsible if your UCCX stops working, your favorite rock band starts taking drugs, etc. Tested with UCCX Premium 7.0SR5 * * *


Solution:


First, add the necessary JAR files to UCCX's classpath and restart the CCX Engine. Yes, it's really, really necessary.

There's a page listing the dependencies of VFS, but if you can't wait:

Apache Commons Logging: http://commons.apache.org/proper/commons-logging/download_logging.cgi

Apache Commons Net:  http://commons.apache.org/proper/commons-net/download_net.cgi

Apache Commons Collections: http://commons.apache.org/proper/commons-collections/download_collections.cgi

Jcraft JSch: http://www.jcraft.com/jsch/

Apache Commons VFS: http://commons.apache.org/proper/commons-vfs/download_vfs.cgi

You can download the other libraries, e.g. JCIFS as well, if you want CIFS support - not discussed in this document.


The UCCX Custom File Configuration page should be similar to this:


vfs-classpath.png



We are going to create a pair of scripts, one for reading and one for writing. The only difference is in the URL.


Reading


1. Create a new UCCX script and insert all the necessary steps (Accept, for instance).


2. Add the following variables:

bais - type java.io.ByteArrayInputStream - initial value null

prompt - type Prompt - initial value P[]

url - type String - enable the Parameter attribute, and use the following format:

FTP: "ftp://username:[email protected]/full/path/to/file.wav"

SFTP: "sftp://username:[email protected]/relative/path/to/file.wav"

Enabling the Parameter attribute is for this demonstration's sake only, it's not considered safe to "present" sensitive information like usernames and passwords on the UCCX admin web. You may even want to implement an added layer of security, by reading the value of the url variable out from a document that has been uploaded to the document repository of UCCX or from another source.


3. Insert a Set step, resulting the value of the variable bais, with the following code block as the value:


{

org.apache.commons.vfs2.FileSystemOptions opts = new org.apache.commons.vfs2.FileSystemOptions();

org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(opts, "no");

org.apache.commons.vfs2.FileSystemManager fsManager = org.apache.commons.vfs2.VFS.getManager();

org.apache.commons.vfs2.FileObject fo = fsManager.resolveFile(url, opts);

org.apache.commons.vfs2.FileContent fc = fo.getContent();

java.io.InputStream is = fc.getInputStream();

int bytesRead = 0;

byte[] b = new byte[1024];

int fcs = new java.math.BigDecimal(fc.getSize()).intValueExact();

java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocate(fcs);

while ((bytesRead = is.read(b,0,b.length)) != -1) { buf.put(b,0,bytesRead); }

fo.close();

org.apache.commons.vfs2.FileSystem fs = fo.getFileSystem();

fsManager.closeFileSystem(fs);

byte[] bytes = buf.array();

return new java.io.ByteArrayInputStream(bytes);

}


What happens here: we instruct VFS not to use strict host key checking, then after getting the VFS manager, resolving the file by the URL, we get its contents, more precisely, the InputStream. We read bytes out of it in 1024 bytes chunks, writing them to a ByteBuffer. Finally, we hand over the byte array wrapped by a ByteArrayInputStream - which, cast to a Document, becomes a Playable object for UCCX.


4. Add a Set step, where this conversion from InputStream to Document takes place: result variable is prompt, value: (Document) bais.


5. Finally, a Play Prompt step, playing prompt.


Here's a screenshot for inspiration:


sftp-read.png


Now, if we assign this script to an application and have an SSH server handy with a WAV file at the location accessible by the user defined in the URL, the script connects downloads the file and plays it.


Don't have an SSH server but there's an FTP server available?

Switching to FTP is changing the value of the URL only: the protocol would be ftp (instead of sftp).



Writing


Creating a file on an SFTP or FTP server becomes a piece of pie using VFS.


1. We will need the following variables:

recording - type Document - intial value DOC[]

returnCode - type int - just a dummy variable

url - type String - again, this is the location of the file we want to create or modify. See the second step of the "Reading" section for more details.


2. Add a Recording step, yielding the value of recording.

Insert a Set step to the Successful branch, with its variable field set to returnCode. Then add the following code block as its value:


{

org.apache.commons.vfs2.FileSystemOptions opts = new org.apache.commons.vfs2.FileSystemOptions();

org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(opts, "no");

org.apache.commons.vfs2.FileSystemManager fsManager = org.apache.commons.vfs2.VFS.getManager();

org.apache.commons.vfs2.FileObject fo = fsManager.resolveFile(url, opts);

org.apache.commons.vfs2.FileContent fc = fo.getContent();

java.io.OutputStream os = fc.getOutputStream();

java.io.InputStream is = recording.getInputStream();

int bytesRead = 0;

byte[] b = new byte[1024];

while ((bytesRead = is.read(b,0,b.length)) != -1) { os.write(b,0,bytesRead); }

os.close();

fo.close();

org.apache.commons.vfs2.FileSystem fs = fo.getFileSystem();

fsManager.closeFileSystem(fs);       

return 0;

}


It's very simple. We take the recording's InputStream and copy bytes to the target file object's OutputStream.


Again, it's very easy to switch to FTP (or any other supported protocol), provided the correct URL format is used.


sftp-write.png



Tips for enahancement:


1. A non-blocking way of reading: take the InputStream of the remote file using VFS and play its contents directly. Cons: voice quality and the need of closing the resource after playing the prompt.

2. Surrounding the code block with a try..catch construct, catching various exceptions and handle them accordingly (for instance, if the file does not exist or it cannot be created).


Enjoy.

Loading.
Sonain Zia Tue, 12/03/2013 - 08:45
User Badges:

Hi,

Is this possible to use same features with UCCX 8.5?


Thank you,


Sonain

Gergely Szabo Tue, 12/03/2013 - 08:59
User Badges:
  • Green, 3000 points or more
  • Community Spotlight Award,

    Member's Choice, December 2015

Well yes, it should work.

Actions

This Document