Friday, December 31, 2010

Scripted data set

Easy. Just create a scripted data source, then attach a data set to it. Its fetch script would look like:

if (XRowNum == 3) {
    row["KEY"] = '1';
    row["VALUE"] = 'Agenti';
    return true;
if (XRowNum == 2) {
    row["KEY"] = '2';
    row["VALUE"] = 'Týmy';
    return true;
if (XRowNum == 1) {
    row["KEY"] = '3';
    row["VALUE"] = 'SkillGrupy';
    return true;
return false;

Friday, December 3, 2010

Dynamic queries in BIRT - query text replacement

var selectedParams = params["calltypes"].value;
var selectedString = "";
for (var counter=0; counter < selectedParams.length; counter++) {
 selectedString += selectedParams[counter] + ",";
selectedString = selectedString.substring(0,selectedString.length-1);
var before = this.queryText.toString();
var injection = "";
injection =  selectedString;
this.queryText = before.replace('53279',injection);

Tuesday, October 12, 2010

Copy files from/to between local and HTTP(s), (S)FTP, local, mail filesystems

Using Apache VFS. Sweet.

package jobs;

import org.apache.commons.vfs.FileFilter;
import org.apache.commons.vfs.FileFilterSelector;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSelectInfo;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileSystemOptions;
import org.apache.commons.vfs.impl.StandardFileSystemManager;
import org.apache.commons.vfs.provider.sftp.SftpFileSystemConfigBuilder;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FileCopyJob implements Job {
    Logger log = LoggerFactory.getLogger(getClass());
    String fileNameStartsWith = null;
    public void execute(JobExecutionContext context) throws JobExecutionException {
        long startPoint = System.nanoTime();"Reading config.");
        JobDataMap data = context.getJobDetail().getJobDataMap();
        String fromDirURL = data.getString("fromDirURL");
        String toDirURL = data.getString("toDirURL");
        fileNameStartsWith = data.getString("fileNameStartsWith");
        log.debug("fromDirURL=                 {}",fromDirURL);
        log.debug("toDirURL=                   {}",toDirURL);
        log.debug("fileNameStartsWith=       {}",fileNameStartsWith);"Instantiating File System Manager (FSM).");
        StandardFileSystemManager fileSystemManager = null;
        FileObject fromDirFileObject = null;
        FileObject toDirFileObject   = null;
        try {
            fileSystemManager = new StandardFileSystemManager();
  "Creating file objects.");
            FileSystemOptions opts = new FileSystemOptions();
            SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(opts, "no");
            fromDirFileObject = fileSystemManager.resolveFile(fromDirURL,opts);
            toDirFileObject = fileSystemManager.resolveFile(toDirURL,opts);
            Boolean fromDirFileObjectExists = fromDirFileObject.exists();
            Boolean toDirFileObjectExists = toDirFileObject.exists();
  "fromDirFileObjectExists? {}", fromDirFileObjectExists);
  "toDirFileObjectExists?   {}", toDirFileObjectExists);
            if (fromDirFileObjectExists==false || toDirFileObjectExists==false) {
                log.error("Please check file paths, either directory does not exist.");
                throw new RuntimeException("Directory does not exists.");
            FileFilter ff = new FileFilter()
                public boolean accept(FileSelectInfo fileInfo)
                    FileObject fo = fileInfo.getFile();
                    return fo.getName().getBaseName().startsWith(fileNameStartsWith);
            FileObject[] foundFiles = null;
            foundFiles = fromDirFileObject.findFiles(new FileFilterSelector(ff));
            if (foundFiles.length < 1 ) {
  "No files found.");   
            } else {
                for (FileObject foundFile:foundFiles) {
                    String baseName = foundFile.getName().getBaseName();
                    String destinationFullName = toDirURL + toDirFileObject.getName().SEPARATOR + baseName;
                    log.debug("Trying to move {} into {}",baseName,destinationFullName);
          "Moved {}",foundFile);
            } // no code beyond this point
        } catch (FileSystemException e) {
            log.error("Something bad happened with the file system you are trying to reach. ", e);
        } finally {
  "Closing FSM.");
        long endPoint = System.nanoTime();"Operation took {} nanoseconds.", (endPoint-startPoint));

Friday, October 1, 2010

Number format and report title in BIRT, programmatically

Like this:
if (params["forexport"].value == 'n') {
this.getStyle().numberFormat = "Percent";
this.helpText = reportContext.getDesignHandle().title;

Tuesday, August 17, 2010

Dynamic query in BIRT

Pretend, I have a dataset backed by the following SQL query:

from ipcc_baA.dbo.Personal_Callback_List pcl
where pcl.FirstName IN ('something')
and pcl.CallStatus <> 'C'

and a parameter, named cctype, type String, returning either value 'HIM' or 'AB'. No duplicate values.

Based on the parameter value, I want to change the query text. For instance, write something into the IN clause within the brackets. Or, change a whole row.

A not elegant, but working way is: using the dataset's beforeOpen method, like this:

if (params["cctype"].value == 'HIM') {
 var inject = '\'H1\',\'H2\',\'H3\',\'H4\',\'I\',\'M\'';
 this.queryText = this.queryText.replace('\'INJECT\'',inject);
if  (params["cctype"].value == 'AB') {
var inject = 'where pcl.AlternateVDN IN (\'reserveA1\',\'reserveA2\',\'reserveA3\',\'reserveB1\',\'reserveB2\')';
 this.queryText = this.queryText.replace('where pcl.FirstName IN (\'INJECT\')',inject);

And of course, modifying the query itself:

from ipcc_baA.dbo.Personal_Callback_List pcl
where pcl.FirstName IN ('INJECT') --neupravovat!!!
and pcl.CallStatus <> 'C'

And voilá, it works. Of course, one should take extra care not to change the original query, otherwise the replace method won't find the needle in the haystack.

The query, in the data set
The beforeOpen method

Wednesday, August 4, 2010

Finding BirtDateTime

I noticed this today: if the SQL dataset column type is DATETIME, and you set it to DateTime in BIRT as well, the report will show it like this (for 2nd August, 2010 00:00:00, Central European Summer Time):

Mon Aug 02 00:00:00 CEST 2010

This is not something Javascript can accept - although I tried the toLocaleString() method and it worked. However, I just needed the date part. toLocaleDateString() did not show anything.

This is where I tried the BirtDateTime class; it accepts the DateTime strings nicely. Now I can do this for instance:["Day"]) + '. ' + BirtDateTime.month(row["Day"],2) + ' ' + BirtDateTime.year(row["Day"]) 

It will show (provided the browser locale is set to Czech):
2. srpna 2010

Friday, July 16, 2010

Transact SQL loop

This came quite handy today:

TRUNCATE TABLE administration.dbo.HalfHours
SET @FROM = '2006-01-01 00:00:00'
SET @TO = '2012-01-01 00:00:00'
INSERT INTO administration.dbo.HalfHours (XDateTime) VALUES (@TEMP);

CREATE INDEX IDX_ONE ON administration.dbo.HalfHours(XDateTime)
SELECT * FROM administration.dbo.HalfHours

Monday, May 17, 2010

Unix time stamp to SQL Server DATETIME

Oh, again. I need to take pills or what.
SELECT DATEADD(s, GETDATE(), '19700101')

Wednesday, April 21, 2010

Prevent double requesting HTTP documents in Cisco CRS

I know this is sort of stupid, but I realized one thing today about the way Cisco CRS (IPCC Express, UCCX, IP IVR, whatever) works. Actually, the documentation says

The Create URL Document step does not issue the HTTP request. The actual request occurs when the document is used by another step, such as the Send Response step or the HTTP Forward step. (Cisco Unified Contact Center Express Editor Step Reference Guide, Release 7.0(1))

And that's true - for ALL the steps that call the document created with the Create URL Document Step. For Instance, if you do this:
1. Create URL Document (from http://ipaddress/something?interesting
2. Create XML Document from the document referenced in 1.
3. Write File from the document referenced in 1
It will actually issue two HTTP requests. Serious.

You say it is obvious? Well, I expected if it has already issued the HTTP request in 2, it has no reason to reissue it in 3. Anyway, now I know.

Tuesday, April 13, 2010

Servlet load sequence

The  load-on-startup sub-element indicates the order in which each servlet should be loaded. Lower positive values are loaded first. If the value is negative or unspecified, then the container can load the servlet at anytime during startup.
Which is kinda cool, I can give 1 for one servlet which will initialize logging, and a higher number for other servlets which also must be autostarted.


Thursday, March 11, 2010

Cisco Interoperability Portal

Wow. I was always looking for these information using the CCO search (which sucks). Now, all the CUCM et al interoperate stuff si well organized at the Cisco Interoperability Portal.

SNMP for Java?

I never knew it existed. But it does. Seems interesting:

Wednesday, March 10, 2010

On MapReduce

This post serves as a starting point for the MapReduce tutorials I have read and found interesting:
General: Introduction to Parallel Programming and MapReduce
Excellent presentation on CouchDB: CouchDB: the last RESTful JSON store you'll ever need
This is good, too, on CouchDB: CouchDB

Oracle ADF Faces

I watched their demo. Oracle ADF Faces is definitely worth a try.
Oracle ADF Faces Rich Client Components
Quick Overview Demo

Tuesday, March 9, 2010

ICM Internet Script Editor

Yeah, I forgot that again. How I hate this.
Anyway, the ICM Internet Script Editor is always available on the Administrative Workstation machine at

Monday, March 1, 2010

jTDS info

After having looked up the hundredth time, I put this here so I find it easily:

What is the URL format used by jTDS?
The URL format for jTDS is:

where is one of either 'sqlserver' or 'sybase' (their meaning is quite obvious), is the port the database server is listening to (default is 1433 for SQL Server and 7100 for Sybase) and is the database name -- JDBC term: catalog -- (if not specified, the user's default database is used)


Wednesday, February 17, 2010

Left padding numbers

Again, a simple task: take a random unsigned integer up to 9999, and return it to the caller, but in a string which is always four characters (even though the random number may be one, two or three characters (like 2, 98, 112; I would be expecting 0002, 0098, 0112).

Took like ten minutes to find the right thing in the docs. But finally.

int randomNumber = randomGenerator.nextInt(9999);
System.out.println(String.format("%04d", randomNumber));


Friday, February 12, 2010


I need a Rich Internet Application framework. In Java, of course.
Thinking about either JavaFX. Or, perhaps Apache Pivot.

Wednesday, January 27, 2010

How to add more swap space on Linux

An easy method if you don't want to repartition the whole disk.

dd if=/dev/zero of=/tmp/swap.img bs=1M count=256

Set up a loop device and connect the dummy file with that device:

losetup /dev/loop3 /tmp/swap.img

Then, it's just formatting the loop device:

mkswap /dev/loop3

It also needs activation:

swapon /dev/loop3

Voilá, there is a new swap partition.

To remove it:

swapoff /dev/loop3
losetup -d /dev/loop3
rm /tmp/swap.img

Enable LAN access for Oracle XE admin web on Ubuntu

Edit your .bashrc file to include the lines:

export PATH

Log out. Log in again.

Then start up sqlplus:

sqlplus sys as sysdba

And then just execute:

SQL> exec dbms_xdb.setListenerLocalAccess (l_access => FALSE);

Wednesday, January 20, 2010

Generating unique file names with SimpleDateFormat and Random

Sometimes it is necessary use an unique file name when writing a voice recording with Cisco IP IVR or Cisco Unified Contact Center Express (UCCX). The CCX Editor does support Java, so we can easily do this: let's just create a timestamp with the date and time including milliseconds, then just append a random number between 0 and 9999. Yes, I know there is still some chance this will generate the same string, but this isn't something I cannot live with.

First, create the timeStamp string using this code block:

String DATE_FORMAT="yyyyMMdd_HHmmss-SSS";
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(DATE_FORMAT);
return sdf.format(new Date());

Then generate a random number with this code:

java.util.Random nahcis = new java.util.Random();
return nahcis.nextInt(9999).toString();

And in the end just concatenate:

"recording_" + timeStamp + "_" + randomNumber

This will create a string like:


And this is something you can use as the file name.

Sunday, January 3, 2010

Servlets cannot look into their own install directory? Solution.

This applies to a Tomcat 5.5 installation running on a Ubuntu 8.04 server (and, all Ubuntu versions I know).

If you write a servlet that needs to read a file which is in the servlet's WEB-INF directory, for instance, a config.xml, Tomcat, or more precisely, the Java Security Manager will not let you.

(Interestingly, Tomcat that comes with CentOS, will do. Hmm.)

What I did is turning off the security manager in the /etc/init.d/tomcat5.5 file.

Task for the rest of the week: find out how to enable the required thing without turning off the Security Manager.

Saturday, January 2, 2010

Using apt behind a proxy in Ubuntu

It's really easy. All you have to do is:

$export http_proxy

and voila, you can apt-get update, apt-get install, whatever.