on 04-02-2012 05:43 AM
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":
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:
(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:
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:
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.
Gergely, I'm most definitely interested in this..let me know how it goes..
First off thank you for this script and I am sorry if I am "Necro'ing an old post, however I am receiving the "not a static method: close;" NPE, so I verified my user and pass account and was able to see that the user was logging into the server via the security log in the event viewer. I then tried removing the Try and Catch lines and then debugged it an am getting the following error
it almost looks like the os = smbFile line is getting passed a null but you can see the smbFileURL in the variables is not null, and from your example it seems correct.
any Ideas?
Hi,
now this may sound a bit silly but are you absolutely sure there is a share named "Recording" on that server? Can you try the following: download the following: http://jcifs.samba.org/src/examples/List.java, compile it with javac. It takes one argument, the SMB URL. Try it with the URL constructed by your script (sans and with the filename). Does it throw an error message?
G.
After compiling it I get javac: file not found: smb:\PUD;user:password@192.168.0.61\Recording (The filename, directory name, or volume label syntax is incorrect)
this indicates that it cant connect to the share, yet if I open explorer and do \\192.168.0.61\Recording it takes me there.
I just wanted to check to see if I could connect to the system I went to a centos box and did the following command smbclient //192.168.0.61/Recording -U Username and them put in the same password I am using in the smb URL and it connected, I am able to list the directory
Hi,
did you try the List java app with the / at the end?
G.
yes but the more I look at it I realize I may not know what I am doing if I compile with javac list.java it creates a list.class not sure how to run the class, unless i am doing something wrong
Hi, alright so you can do (in bold):
greg@antares:/tmp$ java -cp .:./jcifs-1.3.17.jar List smb://user:pass@10.20.30.40/c$/
$Recycle.Bin .grails autounattend.xml.old backup_scripts backup_staging Boot bootmgr bootmgr.efi BOOTSECT.BAK compaq configs cpqsprt.trace cpqsystem Documents and Settings hp inetpub INSTALL msdia80.dll pagefile.sys Program Files Program Files (x86) ProgramData Python33 Recovery reports scripts share smh_installer.log System Volume Information temp tmp Users Windows zl5
35 files in 731ms
greg@antares:/tmp$
This above just listed the contents (files and directories of the c$ default share on 10.20.30.40).
G.
ok I figured it out, the random generated password for the account I was using had a # in it and it was messing up the smb URL
Its ALIVE!!! hehe
Edit: now I am finding it takes 7 seconds to copy a 24k file to the file server, the script paused while it did that anyway to move on while its copying or speed it up?
Hi, you can increase the buffer size (for instance, set 16384 as the value of bufSize).
But the real issue might be something else here and it's most likely the system waiting for name resolution (for example, see this post on SO: http://stackoverflow.com/a/18837754)
So insert the following command into the code block - let it be the first row:
System.setProperty("jcifs.resolveOrder","DNS");
Did it help to speed things up?
G.
I tested this script on version 10.5(1) with jcifs-1.3.18.jar and it worked like a champ.
Thanks for the post!!
I know this is an old post, but it is still a top ranking result on Google.
My last posted comment was from July when I deployed this on a 10.5(1) instance. For others that may come across this, we did an upgrade to 10.5(2) of our CUCM server. We did not upgrade UCCX. However, the CUCM upgrade did update the JTAPI connection to UCCX.
When we restarted the UCCX server, the Application Manager services was left in a "Partial Service" state.
We identified that the script which uses this library was the root cause.
By removing the steps from our script, the UCCX server was then in "In Service" without issue.
Additionally, we could not get the script to trigger a Reactive Debug in UCCX Editor.
So it appears that something in the CUCM 10.5(2) JTAPI update caused this to fail.
I have not found a solution to this yet, but if I do, I will post it back here.
In the meantime, has anyone else found a solution to this, or had a similar experience running 10.5(2)?
Thanks!
Hi Richard,
this might sound silly, but did you try restarting the server?
Also, did you get the logs? There must be a clue somewhere.
Can you tell me the exact version (including the build number) of the UCCX and the CUCM instance? I might give it a try.
Thanks.
G.
G, we setup a lab with CUCM 11 (11.0.1.20000-2) and UCCX 11 (11.0.1.10000-75) to test if this was still an issue, and we were able to get the above to work.
Did not resolve the issue with CUCM 10.5(2) and an earlier (do not have that UCCX version number captured) UCCX.
Hope this helps.
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: