cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
13790
Views
10
Helpful
61
Comments
Gergely Szabo
VIP Alumni
VIP Alumni

Problem:

It's a common requirement to play prompts that are saved as wave files on a Windows share. Starting UCCX 8.x the platform is Linux-based, thus there's no possibility to "mount" Windows shares easily.

A user might want to save prompts recorded by UCCX on to a Windows share.

Analysis:

UCCX must be enabled to communicate using the CIFS/SMB protocol. Also, the file should be cached in order to eliminate delays.

Solution:

**** DISCLAIMER: This is a proof of concept. I did not check whether the LGPL license of the JCIFS library is compatible with UCCX. USE THIS AT YOUR OWN RISK. I am not responsible for any damage this solution may cause. I cannot guarrantee this would work with your installation of UCCX. Tested with IP IVR System version: 8.0.2.11004-12. ****

I. reading a prompt from a Windows share

1. Download the JCIFS library, available at http://jcifs.samba.org/ (click the Download link, then download a recent file, for instance, jcifs-1.3.17.jar

2. Upload this JAR file to UCCX, using the System > Custom File Configuration. After uploading, make sure to select the file as a "Selected Classpath Entries":

uccx_classpath.PNG

3. Restart the CCX engine.

4. Make sure you've got set up the following: the file is saved using a correct codec and format (check System > System Parameters Configuration, look for the Media Parameters section); also, the file must be available in a Windows share (default shares - like c$ - might not work); and of course, you need to have a user and a password.

5. Create a UCCX script. Add all the variables steps you need (for instance, Accept, etc).

6. Add a new variable, of type java.io.ByteArrayInputStream. Name it byteArrayIS (for instance).

7. Add a new variable, of type String. Name it smbFileURL (for instance).

8. Add a new Set step. The result wariable would be smbFileURL. For the value, use the following format:

smb://domain;user:password@ipaddress/share/file.wav

For example:

smb://ipcc;filereader:SuperS3crEt!@10.232.128.68/Temp/friday.wav

This means: ipcc\filereader user with password SuperS3crEt! may read the contents of the file friday.wav on the server 10.232.128.68 within the Temp share.

For more possibilities of this URL, take a look at the JCIFS documentation at http://jcifs.samba.org/src/docs/api/jcifs/smb/SmbFile.html (scroll down to SMB URL Examples).

8. Add a new Set step. The result variable would be byteArrayIS. As for the value, paste in the following closure:

{

int bufSize = 4096; // buffer size, bytes

java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();

try {

    jcifs.smb.SmbFile smbFile = new jcifs.smb.SmbFile(smbFileURL);

    java.io.InputStream in = smbFile.getInputStream();

    byte[] bytesRead = new byte[bufSize];

    int bytesReadLength = 0;

    while(( bytesReadLength = in.read( bytesRead )) > 0 ) {

        baos.write(bytesRead,0,bytesReadLength);

    } //while

    in.close();

    baos.close();

} catch (Exception e) {

return null;

} //try..catch

return new java.io.ByteArrayInputStream(baos.toByteArray());

}

(for more information how this works, look at the end of this section).

9. Add a new Play Prompt step. Use the following value on the Prompt tab:

(Prompt) byteArrayIS

10. Test it. You should be able to hear the contents of the file prompt.

Screenshot:

uccx_smbfilemagic.PNG

(Click the image above to enlarge it).

What just happened?

The closure above dissected:

int bufSize = 4096; // buffer size, bytes

We define the read buffer size. 4096 bytes, in other words, 4 kilobytes seems to be alright, you can modify this to 8KB or 16KB if the file reads slowly.

java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();

Just a ByteArrayOutputStream to hold the prompt contents.

The following try..catch block contains:

jcifs.smb.SmbFile smbFile = new jcifs.smb.SmbFile(smbFileURL);

A new File (or more precisely, a SmbFile) object is constructed, with one parameter: smbFileURL.

Of course, you can do something more with it, for instance, check whether the file exists. Again, take a look at the JCIFS API docs.

java.io.InputStream in = smbFile.getInputStream();

This is where we access the InputStream of the above SmbFile.

byte[] bytesRead = new byte[bufSize];

A byte array, for buffering the file contents. The buffer size is defined in the bufSize local variable.

int bytesReadLength = 0;

Another local variable, for checking how many bytes have been read from the InputStream of the SmbFile object.

And then the only thing we need to do is just read the bytes from the InputStream (in chunks, to give it some speed), and copy these bytes to the ByteArrayOutputStream:

while (( bytesReadLength = in.read( bytesRead )) > 0 ) {

     baos.write(bytesRead,0,bytesReadLength);

} //while

Of course, it's always good to close the resources:

in.close();

baos.close();

The catch block will return null (in case something goes wrong), but you can also include something more useful.

return null;

But if everything goes perfect, we can return a ByteArrayInputStream (by flipping the ByteArrayOutputStream above):

return new java.io.ByteArrayInputStream(baos.toByteArray());

The Play Prompt step (and every prompt-related step) can work perfectly with InputStreams, including ByteArrayInputStreams, the only thing to remember is to cast them to type Prompt. It's easy:

(Prompt) byteArrayIS

So again, we create an InputStream out of the file (which is accessible thanks to the JCIFS library), read the contents of the file in chunks, write these chunks to an OutputStream, finally, we just take the bytearray of this OutputStream, creating an InputStream, which we can hand over to the Play Prompt step.

II. writing a prompt to a Windows share

First, install the JCIFS library, by following the steps 1 to 3 (including) of the previous section (I. reading a prompt from a Windows share).

1. Add these new variables:

  • exitCode, type int, initial value = 0;
  • mydoc, type Document, initial value null;
  • smbFileURL, type String; initial value = "";

2. Create a new script, add all the necessary steps, for example, Accept.

3. Add a new Set step. Assign the value of the smbFileURL variable, following this format:

smb://domain;user:password@ipaddress/share/file.wav

For example:

smb://ipcc;filereader:SuperS3crEt!@10.232.128.68/Temp/friday.wav

This  means: ipcc\filereader user with password SuperS3crEt! may read the  contents of the file friday.wav on the server 10.232.128.68 within the  Temp share.

For more possibilities, please read the JCIFS API docs, at  http://jcifs.samba.org/src/docs/api/jcifs/smb/SmbFile.html (scroll down to SMB URL Examples).

4. Add a new Recording step, that would yield the value of the mydoc variable. Use a prompt of your choice (may be something like "please record your message" etc)

5. Add a new Set step to the Successful recording branch of the previous (Recording) step. Use the variable exitCode. The value would be this closure:

{

java.io.OutputStream os = null;

java.io.InputStream in = null;

try {

int bufSize = 4096;

jcifs.smb.SmbFile smbFile = new jcifs.smb.SmbFile(smbFileURL);

os = smbFile.getOutputStream();

in  = mydoc.getInputStream();

byte[] bytesRead = new byte[bufSize];

int bytesReadLength = 0;

while ((bytesReadLength = in.read(bytesRead)) > 0 ) {

    os.write(bytesRead,0,bytesReadLength);

}

} catch (Exception e) {

return -1;

} finally {

in.close();

os.close();

return 0;

}

}

6. Save your script and test it. A new file, specified in the smbFileURL variable should be created within the Windows share, with the preferred format of UCCX (either G.711 uLaw or G.729 depending on the settings of your UCCX system).

Notice: the value of the exitCode variable will be -1 if, for any reason, an error happens during writing the prompt file. If it is written successfully, the value of the exitCode variable is 0.

Screenshot:

uccx_smbfilemagic_write.PNG

What just happened?

The Recording step created a new recording of type Document. This is just plain ordinary Java object, which is actually a string, or rather, a stream of bytes that equals to the prompt file contents. All we need to do is just create an inputstream of this object, and copy the contents of it to another stream, namely a FileOutputStream of the SMB File we create. The closure also has got an exception catching mechanism, if any exception is raised, it would give back the control to the script with the value -1 of the exitCode variable so you can react on it (instead of just having the script die which may be an embarrassement in most of the cases).

FAQ:

Q1. I'm getting an error in CCX Editor: "error unmarshalling return nested exception is java.net.SocketException". Why?

A1. Make sure you restart the CCX Engine after uploading the JCIFS library and before trying to run the script. Yes, it's really necessary.

Q2. Will Cisco TAC support this?

A2. I don't know, ask them :-) Probably not, since it uses an external library.

Q3. JCIFS is licensed under LGPL. Can it be used with UCCX?

A3. Not sure. I am not a lawyer. Probably yes. But again, I am not sure about it.

Comments
Gergely Szabo
VIP Alumni
VIP Alumni

Hi,

actually, the smbFileURL variable I used in my example is of type String. This means you can modify it very easily.

For instance, if the caller input is 1-1-2 then you can do this:

1. /* collect the digits in a String variable, named "digits" */

2. Set smbFileURL = "smb://server/share/" + digits + ".wav" // this means you actually concatenate three Strings when creating the smbFileURL String variable.

3. /* use smbFileURL as shown above */

So in your case the Set step right after the Successful exit of the Recording step may be written as follows:

Set smbFileURL = "smb://production;wgood:yourpassword@150.3.3.202/audio/" + digits + ".wav"

provided the digits variable of type String exists and it contains some useful information, for example, the caller input.

G.

w.good
Level 1
Level 1

Thank you so much Gergely, that is exactly what I was needing.  I appologize, as this is all new to me, and I am still learning.  So far it was been really exciting...

Thanks Again,

Will

w.good
Level 1
Level 1

Can you recomend any good books or sites that I can use to gain more knowledge about this from ?

Will

voicestudy
Community Member

Hi,

I´m trying to get work "Writing a prompt to a Windows share" script

Actual issue is when execute the script in debugging mode appears this error and the file not save to windows share folder

error1.jpg

I´m using UCCX 8.5

What could be the issue?

Thanks

Gergely Szabo
VIP Alumni
VIP Alumni

Hi,

if there's an NPE, it means something really, really bad has happened in the try..catch block.

Anyway, can you please check whether the SMB share is accessible at all? Are you using the correct JCIFS URL? Is the password correct?

G.

voicestudy
Community Member

Yes, I´m using URL provided in this page, I check SMB share using my XP computer accessing it.I´m  using a Windows 2000 Server to share the folder.

What means "not a static method: close;" ?   (close instruction is not recognized by uccx?)

ccc.jpg

Thanks

Gergely Szabo
VIP Alumni
VIP Alumni

Hi,

that NPE is actually a byproduct of an exception that happened earlier in the try..catch block.

Since everything gets executed in the finally block (regardless of the result), and that in.close() call is in the finally block, it looks like the close() method is being executed on the in object which is null at that moment. That's why it's a NullPointerException :-)

Moving the in.close() call before the catch command will not throw this NPE, but you won't see any files created on your windows share, and it would just silently die.

I am still almost sure there's a problem with that CIFS communication. Can you please post your script or at least the JCIFS URL - in our case, it's the value of the smbFileURL variable.

G.

voicestudy
Community Member

Ok.

script.jpg

Gergely Szabo
VIP Alumni
VIP Alumni

Hi,

can you remove the line starting try {

and the last three lines (starting } catch (Exception

?

Gergely Szabo
VIP Alumni
VIP Alumni

Hi,

I am sorry, the last line should of course close the code block, so the last line must stay as it is: }

G.

voicestudy
Community Member

After remove those lines, appears this message:

error2.jpg

Thanks

Gergely Szabo
VIP Alumni
VIP Alumni

Yeah, I am just stupid. Forgot that return command.

Use this as the last line:

return null; }

You are getting that error because the script actually expects a return value. Returning null works too.

voicestudy
Community Member

After running script modified appears this error:

error3.jpg

Gergely Szabo
VIP Alumni
VIP Alumni

I see. Did you restart the UCCX engine after installing the JCIFS library?

If yes, can you just go and reboot the whole thing? Sometimes it just needs a complete reboot.

voicestudy
Community Member

After reset uccx server, same error appears  -(

Could be UCCX Version ?

error4.jpg

Getting Started

Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community: