Friday, September 29, 2006

BIRT: Passing Parameters from the Command Line for Scheduled Tasks

I received a comment about BIRT report scheduling earlier. Basically the person was having an issue with using a Date parameter in a BIRT report using the command line tools. So here, I am going to replicate the issue the best that I can and make a correction.

First thing I am going to do is create a basic report called ParameterTest. The query I will use for this report is the following:

SELECT
No_emp
FROM
Course_histories
WHERE
Cd_crs = 'CBNAF01'
and cd_ch_status = 'F'
AND dt_ch_compdt > ?

This query will search my course histories table for records where the completion date is above a particular parameter. For the record, the query will be run against an Oracle database, where the dt_ch_compdt field is a date field. I create a report parameter called DATE and bound it to my dataset. Now I save my report. One thing to note is I do not enter a default value for my report parameter so I can easily duplicate the issue. Otherwise, I will get a false sense of accomplishment if my command line parameters are misshapen.

So now that I have my report, I want to try to run it using the same type of format that the requestor did. So I run the following command from my Birt Runtime folder:

C:\birt_runtime\birt-runtime-2_1_0\ReportEngine>genReport.bat -o outp.html -p DATE=20051111 C:\Eclipse\workspace\BirtParameterTest\ParameterTest.rptdesign

C:\birt_runtime\birt-runtime-2_1_0\ReportEngine>ECHO off
Sep 29, 2006 11:46:02 AM org.eclipse.birt.report.engine.api.impl.EngineTask vali
dateScalarParameter
SEVERE: Parameter DATE doesnt allow a null value.
Sep 29, 2006 11:46:02 AM org.eclipse.birt.report.engine.api.ReportRunner runAndR
enderReport
SEVERE: Some required parameter values are not set or set to incompatible data t
ype.
org.eclipse.birt.report.engine.api.EngineException: Some required parameter valu
es are not set or set to incompatible data type.
at org.eclipse.birt.report.engine.api.impl.RunAndRenderTask.run(RunAndRe
nderTask.java:145)
at org.eclipse.birt.report.engine.api.ReportRunner.runAndRenderReport(Re
portRunner.java:237)
at org.eclipse.birt.report.engine.api.ReportRunner.execute(ReportRunner.
java:162)
at org.eclipse.birt.report.engine.api.ReportRunner.main(ReportRunner.jav
a:118)

Hmm, that didn’t work, which is the same error the user reported. Lets try making a small change to the way that the parameter is passed in…

C:\birt_runtime\birt-runtime-2_1_0\ReportEngine>genReport.bat -o outp.html -p "D
ATE=20051111" C:\Eclipse\workspace\BirtParameterTest\ParameterTest.rptdesign

C:\birt_runtime\birt-runtime-2_1_0\ReportEngine>ECHO off
Sep 29, 2006 11:53:02 AM org.eclipse.birt.report.engine.api.ReportRunner evaluat
eParameterValues
SEVERE: the value of parameter DATE is invalid
org.eclipse.birt.core.exception.BirtException: Can not convert the value of 2005
1111 to Date type.
at org.eclipse.birt.core.data.DataTypeUtil.toDate(DataTypeUtil.java:474)

at org.eclipse.birt.core.data.DataTypeUtil.toDate(DataTypeUtil.java:900)

at org.eclipse.birt.core.data.DataTypeUtil.toDate(DataTypeUtil.java:398)

at org.eclipse.birt.report.engine.api.ReportRunner.stringToObject(Report
Runner.java:704)
at org.eclipse.birt.report.engine.api.ReportRunner.evaluateParameterValu
es(ReportRunner.java:669)
at org.eclipse.birt.report.engine.api.ReportRunner.runAndRenderReport(Re
portRunner.java:191)
at org.eclipse.birt.report.engine.api.ReportRunner.execute(ReportRunner.
java:162)
at org.eclipse.birt.report.engine.api.ReportRunner.main(ReportRunner.jav
a:118)
Sep 29, 2006 11:53:02 AM org.eclipse.birt.report.engine.api.impl.EngineTask vali
dateScalarParameter
SEVERE: Parameter DATE doesnt allow a null value.
Sep 29, 2006 11:53:02 AM org.eclipse.birt.report.engine.api.ReportRunner runAndR
enderReport
SEVERE: Some required parameter values are not set or set to incompatible data t
ype.
org.eclipse.birt.report.engine.api.EngineException: Some required parameter valu
es are not set or set to incompatible data type.
at org.eclipse.birt.report.engine.api.impl.RunAndRenderTask.run(RunAndRe
nderTask.java:145)
at org.eclipse.birt.report.engine.api.ReportRunner.runAndRenderReport(Re
portRunner.java:237)
at org.eclipse.birt.report.engine.api.ReportRunner.execute(ReportRunner.
java:162)
at org.eclipse.birt.report.engine.api.ReportRunner.main(ReportRunner.jav
a:118)

Ha, we are getting closer, the error now indicates it accepted DATE, but the format is incorrect. BIRT accepts dates in a peculiar format, and by peculiar, I mean not in the standard Oracle date format that I am used to. While I have made mention of this in presentations on BIRT, I don’t believe I have ever written about it on here. So in order to pass in the date of 01-JAN-2006 in Oracle, in BIRT it needs to be formatted as “01/01/2006”. So lets make that change to the genReport call and see what happens…

C:\birt_runtime\birt-runtime-2_1_0\ReportEngine>genReport.bat -o outp.html -p "DATE=01/01/2005" C:\Eclipse\workspace\BirtParameterTest\ParameterTest.rptdesign

C:\birt_runtime\birt-runtime-2_1_0\ReportEngine>ECHO off
C:\birt_runtime\birt-runtime-2_1_0\ReportEngine>dir outp.html
Volume in drive C is C21A216
Volume Serial Number is 8CAE-547F

Directory of C:\birt_runtime\birt-runtime-2_1_0\ReportEngine

09/29/2006 11:56 AM 906,406 outp.html
1 File(s) 906,406 bytes
0 Dir(s) 32,806,092,800 bytes free

And sure enough, that corrected the problem. So the solution is to enclose your parameters in double-quotes, and to format any dates accordingly.

Thursday, September 28, 2006

Actuate: Changing a Rows Color with a Button

I was incredibly surprised to get an Actuate question thrown my way, and a good one at that. I normally don’t get Actuate questions, so I was a little excited to try to figure this one out.

The question was “How to Change a row color after clicking a button in Actuate?”

This was not as easy as I thought it would be. My first instinct was to try to override a method for to force a color change, however I ran into a few issues. First, was IServer strips out Basic events when it renders online. So if I was to publish this report online, I would not be able to use the overridden method. Also, I would need a way to force a refresh of the page without recalling the entire report, which there doesn’t seem to be a way to do this using Actuate Basic.

Fortunately, however, Actuate provides the Browser Scripting component. Since reports are essentially HTML files, a little JavaScript and some custom overridden methods to provide tags can go a long way in solving this issue.

For this example, I am using a real simple report to pull all the roles that a particular Actuate user has in the system. The report is driven off of a basic select query. There is a single JavaScript function that will receive as a parameter the ID of the calling object. It will take that object ID, create an Element object and set to the calling object. It then goes up the DOM tree to the parent of the calling object; checks the parent children nodes for an object with a Style tag set to the background color, then change that background color. For more complex reports, a little more logic and care would be called for in order to select the correct object to set. The key to getting this to trigger correctly is to be sure to explicitly set the background color of the row to change the color.

The second browser scripting control is simple creates a button. What I did is I overrode the Browser Scripting Controls “BrowserCode” method to return a button with an ID that is specifically set to an easy to pass value for the search function defined above. In order to uniquely define each button, I have a global parameter that gets incremented on each BrowserCode function call and uses that in the ID. It doesn’t need to be exact, or sequential, just unique.


Figure 1. Workspace


Figure 2. Setting a Global Parameter


In Figure 1 above you can see the report design. It is very basic. Notice the two browser scripting controls. I put one in the Before report containers content section. This is here since the query driving this section will only return 1 result. This is where the JavaScript function is defined at. This could actually go into the page header, or the master layout. I kept it here out of pure laziness since I didn’t want to change views. Below is the code for the browser scripting control:
<script language="JavaScript" type="text/javascript">
function ChangeRowColor(callingObjectID)
{
//Get the marker tag for the clicked on button
var childElement = document.getElementById(callingObjectID);
//Variable to hold the node witht he found background attributes to change
var foundNode = null;
//Generic counter x
var x;

//Set the parent element of the caller
var parentElement = childElement.parentNode;

//Go up the DOM tree while we have not found our target and we have not reached the root DOCUMENT element
while ((!foundNode) && (parentElement.nodeName != "#document"))
{
//Go through each child element in the current parent, check if it has a style element defined and a background element. If so,
//then we found our target. This will find the first element to match these conditions only, so be sure to keep other surrounding elements
//background at default, which "usually" (not always) does not include them in the output
for (x = 0; x < parentElement.childNodes.length; x++)
if ((!foundNode) && (parentElement.childNodes[x].style) && (parentElement.childNodes[x].style.backgroundColor))
{
foundNode = parentElement.childNodes[x];
//alert("Found!");
break;
}

//we went through all the child elements and did not find our result, move up the tree
if (!foundNode)
parentElement = parentElement.parentNode;
}

//After going through the DOM tree, if we found our element, then change the background color to our predefined background color. Otherwise, set it to
//the white
if (foundNode)
{
alert(foundNode.id + " - " + foundNode.style.backgroundColor);
if (foundNode.style.backgroundColor == "#ffffff")
foundNode.style.backgroundColor = "#B3D9E6";
else
foundNode.style.backgroundColor = "#ffffff";
}
}
</script>

In the details band is where I stuck the second BrowserScripting control with the button definition. The overridden BrowserCode function looks like so:

Function BrowserCode( ) As String
BrowserCode = Super::BrowserCode( )
' Insert your code here
BrowserCode = "<input name='Submit' id='Button" & CurrentRowNum & "' type='submit' onclick='ChangeRowColor(this.id);' value='Submit' />"
CurrentRowNum = CurrentRowNum + 1
End Function


Figure 3. Set Background Color

Now the final piece of the puzzle that makes this whole thing work, be sure to set the element whose color you want to change to your default background color, in my case White. Figure 3 illustrates this. The reason this works is the browser control with the button and the text field should reside in the same DIV tag. When the function goes to the parent element, it should be the DIV tag, then search the DIV tags children and it will find the element with the background color set, which is the text field. The Text Field needs to be the first element with a background color tag also; otherwise the wrong object will change color.

Now, I publish the report to IServer and preview the results. Figure 4 shows the finished report. Now, when I click on one of the buttons, the corresponding rows color will change. This can also work if the rows text is a hyperlink. Then, you can bypass the entire search process and just use the calling objects ID.

Figure 4. Finished Report

Tuesday, September 26, 2006

Visual Basic Script: Don't Assume Your VB Constants are Available in Your Scripts

From the enough to drive you crazy department…

I was working on a small script for a system we have. The goal was to import from an Access database into Oracle. The catch, one field in the Access database is importing into a CLOB field in Oracle. It looks something like this:

Access:
     TableWithMemo
          Whatever (TexT)
          the_field (Memo)
Oracle:
     Test
          F1( varchar2(255)
          F2(CLOB)

So I wrote the following VB program:
Sub main()
    'The recordset objects for Access and oracle
    Dim access_con As New ADODB.Connection
    Dim access_com As New ADODB.Command
    Dim access_rs As ADODB.Recordset
  
      
    Dim oracle_con As New ADODB.Connection
    Dim oracle_com As New ADODB.Command
    Dim oracle_rs As New ADODB.Recordset
    
        
    'open the Access Database
    access_con.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=R:\ db2.mdb;Persist Security Info=False"
    access_con.CursorLocation = adUseClient
    access_con.Open
      
    access_com.ActiveConnection = access_con
    access_com.CommandType = adCmdText
    access_com.CommandText = "select * from TableWithMemo"
    
    Set access_rs = access_com.Execute
    
    'Now open the Oracle database. The key to getting the CLOB/BLOB fields is using the Oracle OleDB data provider
    'instead of the Microsoft one. If you use the Microsoft one, you will get a "Data Type Not Supported" error
    'also, I was getting some goofy errors having to do with the update statement, so I changed the connection type
    'to a Client Side Cursor and opened in Read/Write mode
    oracle_con.ConnectionString = "Provider=OraOLEDB.Oracle.1;Password=$$$$;Persist Security Info=True;User ID=$$$$;Data Source=tsqa"
    oracle_con.CursorLocation = adUseClient
    oracle_con.Mode = adModeReadWrite
    oracle_con.Open
    
    oracle_com.ActiveConnection = oracle_con
    oracle_com.CommandType = adCmdTable
    oracle_com.CommandText = "TEST"
    
    'I also set the locktype to optimistic to deal with the fail on update
    oracle_rs.LockType = adLockOptimistic
    oracle_rs.Open oracle_com
    
    'yeah yeah, I know I don't need to check equality for a boolean... clarity and all that
    If (oracle_rs.Supports(adAddNew) = False) Then
        MsgBox ("This recordset cannot update")
        oracle_con.Close
        access_con.Close
        
        Set oracle_rs = Nothing
        Set oracle_com = Nothing
        Set oracle_con = Nothing
        
        Set access_rs = Nothing
        Set access_com = Nothing
        Set access_con = Nothing
        
        Exit Sub
    End If
      
    'Pull in as clob
    While Not (access_rs.EOF)
        oracle_rs.AddNew
        
        oracle_rs("f1") = access_rs("whatever")
        oracle_rs("f2") = access_rs("the_field")
        
        oracle_rs.Update
        
        access_rs.MoveNext
    Wend
    
    oracle_rs.Close
    oracle_con.Close
    
    access_con.Close
    
    Set oracle_rs = Nothing
    Set oracle_com = Nothing
    Set oracle_con = Nothing
    
    Set access_rs = Nothing
    Set access_com = Nothing
    Set access_con = Nothing
End Sub

Great, now with 1 problem. It was requested that I make this a VBScript file so it can easily be edited. “No Problem” I think to myself, so I spit out the following modification:

    'The recordset objects for Access and oracle
    Dim access_con
    Dim access_com
    Dim access_rs
    Set access_con = CreateObject("ADODB.Connection")
    Set access_com = CreateObject("ADODB.Command")
    
    Dim oracle_con
    Dim oracle_com
    Dim oracle_rs
    Set oracle_con = CreateObject("ADODB.Connection")
    Set oracle_com = CreateObject("ADODB.Command")
    Set oracle_rs = CreateObject("ADODB.Recordset")
    
    'open the Access Database
    access_con.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=R:\db2.mdb;Persist Security Info=False"
    access_con.CursorLocation = adUseClient
    access_con.Open
      
    access_com.ActiveConnection = access_con
   'access_com.CommandType = adCmdText
    access_com.CommandText = "select * from TableWithMemo"
    
    Set access_rs = access_com.Execute
    
    'Now open the Oracle database. The key to getting the CLOB/BLOB fields is using the Oracle OleDB data provider
    'instead of the Microsoft one. If you use the Microsoft one, you will get a "Data Type Not Supported" error
    'also, I was getting some goofy errors having to do with the update statement, so I changed the connection type
    'to a Client Side Cursor and opened in Read/Write mode
    oracle_con.ConnectionString = "Provider=OraOLEDB.Oracle.1;Password=$$$$;Persist Security Info=True;User ID=$$$$;Data Source=tsqa"
    oracle_con.CursorLocation = adUseClient
    oracle_con.Mode = adModeReadWrite
    oracle_con.Open
    
    oracle_com.ActiveConnection = oracle_con
    oracle_com.CommandType = adCmdTable
    oracle_com.CommandText = "test"
    
    'I also set the locktype to optimistic to deal with the fail on update
    oracle_rs.LockType = adLockOptimistic
    oracle_rs.Open oracle_com
    
  
    'Pull in as clob
    While Not (access_rs.EOF)
        oracle_rs.AddNew
        
        oracle_rs("f1") = access_rs("whatever")
        oracle_rs("f2") = access_rs("the_field")
        
        oracle_rs.Update
        
        access_rs.MoveNext
    Wend
    
    oracle_rs.Close
    oracle_con.Close
    
    access_con.Close
    
    Set oracle_rs = Nothing
    Set oracle_com = Nothing
    Set oracle_con = Nothing
    
    Set access_rs = Nothing
    Set access_com = Nothing
    Set access_con = Nothing

Well, this causes the script to bomb constantly with the following errors:

ADODB.Connection: Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another.

So after banging my head against the monitor for about an hour, it finally dawns on me. The constants do not exist in scripting. So I need to change all of my parameter assignments (CursorLocation, Mode, CommandType) to their numeric equivilents, or assign constants for each. In the end, the following code worked like a champ for me:

    'The recordset objects for Access and oracle
    Dim access_con
    Dim access_com
    Dim access_rs
    Set access_con = CreateObject("ADODB.Connection")
    Set access_com = CreateObject("ADODB.Command")
    
    Dim oracle_con
    Dim oracle_com
    Dim oracle_rs
    Set oracle_con = CreateObject("ADODB.Connection")
    Set oracle_com = CreateObject("ADODB.Command")
    Set oracle_rs = CreateObject("ADODB.Recordset")
    
    'open the Access Database
    access_con.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=R:\db2.mdb;Persist Security Info=False"
    access_con.CursorLocation = 3
    access_con.Open
      
    access_com.ActiveConnection = access_con
    access_com.CommandType = 1
    access_com.CommandText = "select * from TableWithMemo"
    
    Set access_rs = access_com.Execute
    
    'Now open the Oracle database. The key to getting the CLOB/BLOB fields is using the Oracle OleDB data provider
    'instead of the Microsoft one. If you use the Microsoft one, you will get a "Data Type Not Supported" error
    'also, I was getting some goofy errors having to do with the update statement, so I changed the connection type
    'to a Client Side Cursor and opened in Read/Write mode
    oracle_con.ConnectionString = "Provider=OraOLEDB.Oracle.1;Password=$$$$;Persist Security Info=True;User ID=$$$$;Data Source=tsqa"
    oracle_con.CursorLocation = 3
    oracle_con.Mode = 3
    oracle_con.Open
    
    oracle_com.ActiveConnection = oracle_con
    oracle_com.CommandType = 2
    oracle_com.CommandText = "test"
    
    'I also set the locktype to optimistic to deal with the fail on update
    oracle_rs.LockType = 3
    oracle_rs.Open oracle_com
    
  
    'Pull in as clob
    While Not (access_rs.EOF)
        oracle_rs.AddNew
        
        oracle_rs("f1") = access_rs("whatever")
        oracle_rs("f2") = access_rs("the_field")
        
        oracle_rs.Update
        
        access_rs.MoveNext
    Wend
    
    oracle_rs.Close
    oracle_con.Close
    
    access_con.Close
    
    Set oracle_rs = Nothing
    Set oracle_com = Nothing
    Set oracle_con = Nothing
    
    Set access_rs = Nothing
    Set access_com = Nothing
    Set access_con = Nothing

While scripting wasn’t a bad way to go, I assumed that constants had been defined despite the lack of includes. My mind fell victim to the fact that I was working in a VB syntax, so I assumed that the same VB rules applied to scripting as it does to the full fledged environment. After some searching, this appears to be a problem when working with ASP as well. How strange that with all the questions that were asked along these lines, I only found a handful or correct answers. If you are working in ASP, be sure to include the ADOASP.INC file with your projects if your expecting the constants to work.

Monday, September 25, 2006

Subversion: Further Adventures

Since my recent exploits with Subversion have turned my working world upside down, I figured I would share my recent exploits. First, I came across Subclipse, a Subversion plug-in for Eclipse. This opened up the virtual Pandora’s Box, so to speak. With the ability to link all of my Eclipse projects to Subversion repositories and share work with my team, I went on a tirade of downloading every single Eclipse plug-in for my day-to-day duties. I found DBEdit for working with straight SQL, CFEclipse for our Coldfusion applications, the Eclipse Pl-SQL Editor, and a few others. Previous, Eclipse was something I played with on the side when I wasn’t doing real work, or when I wanted to do some decent Java development away from Netbeans, or something forces upon me to do BIRT Development. No more. All these recent additions to my Eclipse plug-in library have lead me to setting up a local Subversion setup on our development file share. No having to fight with TI, submit work order, waiting 6 – 8 months to even get a tech assigned or any of the other hassles associated with our technology group, just install the client, setup the repository, and use the file share that my entire team has access to, this is something I can do myself. All of sudden, Eclipse has become a central part of my life. Amazing how views can change in a day just by being able to leverage a particular tool. Strange thing is, although Eclipse has native CVS support built right in, it just didn’t tickle my fancy the same way. Something just seems… cleaner about Subversion. I think next I will look at the Subversion plug-ins for Visual Studio and see where these lead me.

Saturday, September 23, 2006

My First Experience with Subversion

For some time now I have wanted to play around with SubVersion. I have a college professor friend of mine who raved about it when it first came out some years ago. Finally, Scott Rosenbaum of the BIRT PMC convinced me to take a look at it a little more in depth.

The following instructions are centered around an Ubuntu 6.06 platform, so modify as you will for your platform of choice. First thing to do is to install Subversion. Using the Apt, it is a piece of cake. I drop to a root console and run “apt-get -f install subversion”, as indicated below:

root@digiassn-laptop:/usr/sbin# apt-get -f install subversion
Reading package lists... Done
Building dependency tree... Done
The following extra packages will be installed:
libsvn0
Suggested packages:
subversion-tools db4.3-util
The following NEW packages will be installed:
libsvn0 subversion
0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
Need to get 716kB of archives.
After unpacking 4342kB of additional disk space will be used.
Do you want to continue [Y/n]?
Get:1 http://us.archive.ubuntu.com dapper/main libsvn0 1.3.1-3ubuntu1 [513kB]
Get:2 http://us.archive.ubuntu.com dapper/main subversion 1.3.1-3ubuntu1 [203kB]Fetched 716kB in 2s (269kB/s)
Selecting previously deselected package libsvn0.
(Reading database ... 112788 files and directories currently installed.)
Unpacking libsvn0 (from .../libsvn0_1.3.1-3ubuntu1_i386.deb) ...
Selecting previously deselected package subversion.
Unpacking subversion (from .../subversion_1.3.1-3ubuntu1_i386.deb) ...
Setting up libsvn0 (1.3.1-3ubuntu1) ...

Setting up subversion (1.3.1-3ubuntu1) ...

One thing to note is I believe Apache 2 may be a pre-requisite or optional package. Since I already have Apache 2 and Tomcat installed for use with BIRT, these were not shown in the above transcript. Cool, now that it is setup, I noticed that there are not executables for it... strange. So I read the Subversion quick-start guide to get an idea of what I am doing. Aha, so there are command line tools and they are prefixed as svn... so we have an “Svn” to interact with Subversion, and an svnadmin to administer Subversion.

Here, I want to create a repository containing all my BIRT reports for a project I am working on. The first thing I do is create that repository using svnadmin. I want to call this repository “BirtReports” and store them under my home directory at /home/digiassn. Using my general account, I run the commands like so:

digiassn@digiassn-laptop:/etc/default$ svnadmin create /home/digiassn/BirtReports
digiassn@digiassn-laptop:/etc/default$ cd /
digiassn@digiassn-laptop:/$ cd home/digiassn/BirtReports/
digiassn@digiassn-laptop:~/BirtReports$ ls -alF
total 36
drwxr-xr-x 7 digiassn digiassn 4096 2006-09-23 18:21 ./
drwxr-xr-x 56 digiassn digiassn 4096 2006-09-23 18:21 ../
drwxr-xr-x 2 digiassn digiassn 4096 2006-09-23 18:21 conf/
drwxr-xr-x 2 digiassn digiassn 4096 2006-09-23 18:21 dav/
drwxr-sr-x 5 digiassn digiassn 4096 2006-09-23 18:21 db/
-r--r--r-- 1 digiassn digiassn 2 2006-09-23 18:21 format
drwxr-xr-x 2 digiassn digiassn 4096 2006-09-23 18:21 hooks/
drwxr-xr-x 2 digiassn digiassn 4096 2006-09-23 18:21 locks/
-rw-r--r-- 1 digiassn digiassn 229 2006-09-23 18:21 README.txt

OK, so I have a tool that creates a bunch of arbitrary directories. Well, not necessarily. It actually uses its own database to store your versioned files, so when I import them into Subversion, i wont get to see them. So, I want to check in my BIRT reports, which are stored at /home/digiassn/eclipse/workspace/OldReports. To check these files into Subversion, I can run the following command:

digiassn@digiassn-laptop:~/BirtReports$ svn import /home/digiassn/eclipse/workspace/OldReports file:///home/digiassn/BirtReports -m "BirtProject Initial Import"
Adding /home/digiassn/eclipse/workspace/OldReports/EventsSummaryByIP.rptconfig
Adding /home/digiassn/eclipse/workspace/OldReports/GetIPSummary.rptconfig
Adding /home/digiassn/eclipse/workspace/OldReports/sguilLibrary.rptlibrary
Adding /home/digiassn/eclipse/workspace/OldReports/eventsByStatus.rptdesign
Adding /home/digiassn/eclipse/workspace/OldReports/GetStatus.rptdesign
Adding /home/digiassn/eclipse/workspace/OldReports/.project
Adding /home/digiassn/eclipse/workspace/OldReports/DemoForBook
Adding /home/digiassn/eclipse/workspace/OldReports/IpEventDetail.rptconfig
Adding (bin) /home/digiassn/eclipse/workspace/OldReports/sguil_logo2.png
Adding /home/digiassn/eclipse/workspace/OldReports/GetStatusInfo.rptconfig
Adding /home/digiassn/eclipse/workspace/OldReports/EventSummary.rptdesign
Adding /home/digiassn/eclipse/workspace/OldReports/eventsByStatus.rptconfig
Adding /home/digiassn/eclipse/workspace/OldReports/GetStatus.rptconfig

Committed revision 1.

Sweet, now I've stored my old reports into Subversion. So lets say I want to check these files out now. I can run the command below:

digiassn@digiassn-laptop:/$ cd /tmp
digiassn@digiassn-laptop:/tmp$ svn checkout file:///home/digiassn/BirtReports/
A BirtReports/EventsSummaryByIP.rptconfig
A BirtReports/GetIPSummary.rptconfig
A BirtReports/GetStatus.rptdesign
A BirtReports/eventsByStatus.rptdesign
A BirtReports/sguilLibrary.rptlibrary
A BirtReports/.project
A BirtReports/DemoForBook
A BirtReports/IpEventDetail.rptconfig
A BirtReports/sguil_logo2.png
A BirtReports/GetStatusInfo.rptconfig
A BirtReports/EventSummary.rptdesign
A BirtReports/GetStatus.rptconfig
A BirtReports/eventsByStatus.rptconfig
Checked out revision 1.
digiassn@digiassn-laptop:/tmp$ cd BirtReports/
digiassn@digiassn-laptop:/tmp/BirtReports$ ls
DemoForBook EventsSummaryByIP.rptconfig GetStatusInfo.rptconfig IpEventDetail.rptconfig
eventsByStatus.rptconfig EventSummary.rptdesign GetStatus.rptconfig sguilLibrary.rptlibrary
eventsByStatus.rptdesign GetIPSummary.rptconfig GetStatus.rptdesign sguil_logo2.png

What was easy enough. So, back to the problem that triggered this. I wanted to checkout the files that Scott had on his super secret server so I can view a project he was working on. This was put online using a web server for viewing, so below is the transcript of how I pulled the project in question.

digiassn@digiassn-laptop:/tmp$ cd /tmp
digiassn@digiassn-laptop:/tmp$ mkdir gwt
digiassn@digiassn-laptop:/tmp$ cd gwt
digiassn@digiassn-laptop:/tmp/gwt$ svn checkout http://scotts_super_secret_server/gwt
A gwt/DynaTable
A gwt/DynaTable/.classpath
A gwt/DynaTable/.project
A gwt/DynaTable/src
A gwt/DynaTable/src/com
...remaining files removed for brevity.
Checked out revision 28.

Pretty neat. I have seen CVS systems that can do the same thing, however. By looking in the documentation, there are tons of options for how to run Subversion. You can run a daemon to allow multiple repositories, add user authentication, and integrate authentication with SSH, plus a whole slew of other features.

Thursday, September 21, 2006

VB.Net: Creating and Consuming a Basic Web Service

I thought it would be fun to start experimenting with Web services a little more. I have, for a while now, been fascinated with the concept of web services and the possibilities they provide. From a business perspective, I can create a web service that is normally provided by a report, let’s say something along the lines of retrieving a transcript for a particular employees training, or for a group of employees for that matter. But, before I can walk, I need to learn to crawl.

For this example, I used the Microsoft Visual Web developer 2005 Express edition to create both the web services used in this example and the client. The requirements to run are an IIS server running .Net 2.0. The web service itself will provide 4 functions:

HelloWorld
Add
Subtract
Divide

Hello world is self explanatory. The remaining 3 functions provide their Integer math equivalent operations and return the results. The client program will only consume two of these functions, the Add function and the Hello World function. The idea is that the client will have 3 text boxes, two will be parameters and the third textbox will display the results. When the user clicks on the button, an alert box will display the results of the Hello World function.

To create the web service itself, from within VWD (Visual Web Designer), I went to file, New Web Site. In the dialog that popped up, I selected ASP .Net Web Service, chose a location, and kept the language as Visual Basic .Net.

The page that comes up is the skeleton of the web service. Fortunately, the Hello World function is already provided. I added the remaining 3 functions, to have the service.vb file below:

Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols

<WebService(Namespace:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class MyService
    Inherits System.Web.Services.WebService

    'The generic Hello World function.
    <WebMethod()> _
    Public Function HelloWorld() As String
        Return "Hello World"
    End Function

    'Add two numbers together
    <WebMethod()> _
    Public Function Add(ByVal Num1 As Integer, ByVal Num2 As Integer) As Integer
        Return Num1 + Num2
    End Function

    'Subtract two numbers
    <WebMethod()> _
    Public Function Substract(ByVal Num1 As Integer, ByVal Num2 As Integer) As Integer
        Return Num1 - Num2
    End Function

    'Divide two numbers
    <WebMethod()> _
    Public Function Divide(ByVal Num1 As Integer, ByVal Num2 As Integer) As Integer
        Return Num1 / Num2
    End Function
End Class

You might notice the WebMethod tags. These indicate that the functions are exposed. The other stuff is all auto-generated, so I couldn’t tell you what it does, all I know is it works. To test this, I posted this to my IIS server using the option under the Website/Copy WebSite menu. In the following dialog box, I selected my local IIS server, chose connect, and clicked the button to create a new Web Application.

Once posted, I connected via IE to test, and saw the exposed web services. Now I want to create the client. Using the same project, I confirmed that I did not need to add a new Web Reference. If this was a new project, I would go up to the WebSite/Add Web Reference menu, and point to <webroot>/<project folder>/Service.asmx. It is possible to use the WSDL file as well; however I did not use this in verification.

Graphically the client is simply 3 text boxes and a button. The code looks like so:

<%@ Page Language="VB" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        'Instantiate the web service object
        Dim ms As New MyService
        
        'Set the results of textbox 3 to the result of the add method in the web service
        TextBox3.Text = ms.Add(TextBox1.Text, TextBox2.Text)
          
        'Create a temporary string containing the script to use a JavaScript popup box
        'Note: I had to seperate the < /script > tag into 3 tagsdue to some strange error where
        'VWD kept wanting
        'to use the that tag to end the code block inside of the web page. Very strange.
        Dim strScript As String = String.Format("<script language='JavaScript'>alert('{0}');<" & "/" & "script>", ms.HelloWorld)
        
        'Check if the script has been registered with the page.
        If (Not ClientScript.IsStartupScriptRegistered("clientScript")) Then
            'Use the newer .Net 2.0 ClientScript.RegisterStartupScript function to
            'register the script. I am using Button1s type as the type indicator
            'however since only 1 object is doing this, I could have used any and it
            'would not have mattered
            ClientScript.RegisterStartupScript(Button1.GetType, "clientScript", strScript)
        End If
    End Sub
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
            <br />
            <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox><br />
            <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" />
            <br />
            <br />
            <asp:TextBox ID="TextBox3" runat="server"></asp:TextBox></div>
    </form>
</body>
</html>

Some interesting thing to note in the client. First, I am demonstrating how you can call a JavaScript pop-up/alert function from within ASP .Net. This was difficult to find. In searching for this and implementing it, I came across an error with VWD where the </script> tag in the string caused compilation to fail with the error “Statement cannot appear within a method body. End of method assumed”. I thought this was odd since it is inside of a string literal, however VWD did not agree with me. So the solution was to split up the string using concatenations. I wasn’t too thrilled with this since it makes the code look ugly, but I didn’t really have any choice.

Once posted, the program worked like a baby. I think I will see how difficult it is to do the same things with Java next and compare the difficulty between the two. From there, I think I will try to implement a database driven web service.

Saturday, September 09, 2006

Linux: Working with DocBook

I've been reviewing a manuscript for Prentice-Hall. Originally they sent me a PDF file with the entire book. This was great for me since it was fairly portable. However, due to a unexpected hardware glitch (aka Hard Disk crash), I lost that manuscript. When I requested another, they sent me a series of XML files, which as it turns out, are DocBook formatted. I have never worked with DocBook before, so I needed some way to make these files usable for me.


When I went to parse the documents using the DocBook tools available under Ubuntu, I found a build.xml file, which is the tell-tale sign of an Ant build file. This changes everything. So I verified that this was indeed an Ant build file when I opened the document. To install Ant I ran the following command:


sudo apt-get -f install ant


Once done, I ran ant in the books source folder, however I received an error about missing the Apache FOP packages. When I looked inside of the build.xml file, I found that the version of FOP expected was 0.20.5. This was available from here.


There were also some other dependencies, such as the Java Advanced Imaging SDK, and the proper DocBooks XSL files that the build was expecting (the versions installed with apt-get proved to be inappropriate). So I had had to grab the following packages:


JAI 1.1.2.01: http://java.sun.com/products/java-media/jai/

DocBooks (Latest) and DocBooks XSL 1.68.1: http://sourceforge.net/projects/docbook/


Once I correctly modified the build.xml file to point to the appropriate locations for these libraries (it was previously pointing to folders under a Windows directory structure, and since I am running Ubuntu, that didn't quite translate correctly), it compiled without a problem using the PDF option. I ended up with a Pdf file so now I can review the book as it is intended to look in print.


This proved to be a bit educational. I had read about using LaTeX for typesetting before, but this was my first hands-on experience with an actual setup for one. This was a bit of an eye opener for me since I had previously just thought that modern books were just done from a word processor and went straight to print that way, however that doesn't seem to be the case. It seems kind of strange that making a copy of a book is somewhat like compiling a program in the sense that it uses Build files, Java classes and such. Very interesting.


DIY: How to Remove Logos Using Sugar

I found the following article on Slashdot today. Its a link to this story telling you how to remove vendor logos from products. I thing this is great. I am going to remove all the logos from all electronics and promotion products that I own.


Also interesting from the host site, instructions on how to build your own stripper pole... Now I can live out my fantasy of... wait, thats none of your business. But there are other fun little DIY projects on the site like the volumetric projector (fog machine and lens to create a Star Wars like hologram), and the SNES to Parrallel port adapter (a project that has been around for years, I actually built one in 2001 for “work”. IMHO, the SNES pad was the best game controller ever, plus there are tons of software for controlling it, and IIRC, the Linux Kernel has this already built in). Interesting site.

Friday, September 08, 2006

General: The Digital Voice, Year One

This weekend, Sept. 9th, marks the 1 year birthday of The Digital Voice. What started out as a site that was dedicated to information for beginners in the area of programming, network security, and technology in general, has turned into my personal notepad for projects that I feel would be interesting to share with the community in general. Lots of interesting things have come about from the site, and I hope that individuals find it informative. So feel free to join me in a big Happy Birthday for the site :)

Thursday, September 07, 2006

Linux: Issue Mounting a Windows XP Share with Samba

I had an incredibly strange issue come up when I was trying to mount a Windows XP file share from Knoppix 5. When I would mount the share using the following command:

mount –t smbfs //windows up address/ /mnt/share -o username=username

The command would complete successfully, however whenever I went into the share, I would get the following error:

root@tty1{mnt]# cd /mnt/share
root@tty1 [share]# ls
ls: .: Permission denied


Even stranger, when I did a full list on that share post-mount, I would get ? for the permissions, owner, group, size, and date. Turns out, there is an issue with Windows 2000, 2-3k, and XP file shares and certain Client Signing (at least that is what this article indicates). If this is the issue, then the article submitters symptoms were different than the one I encountered.

This was corrected with the inclusion of using CIFS instead of SMBFS. So I could run the following command instead:

mount –t cifs //windows up addres/share name /mnt/share -o username=username

And now I have access to my share.

Wednesday, September 06, 2006

Security: Defeating Device Lock

You know, I must admit a bit of shock at how easy this was. I had an issue come up where I needed to create an Emergency Repair Disk from a system I had cloned into a VMWare instance. The problem was that the partition didn’t clone 100 percent, and there were errors that kept it from booting. When I went back to the source system, DeviceLock was keeping me from creating the repair disk.

For those of you who don’t know, DeviceLock is an attempt to create a little bit of physical security on a system by denying access to local devices based on domain policy restrictions. In order to get access to a device, you must be on the White List. This includes CD-Rom drives, USB devices, and unfortunately, floppy drives. It is a very powerful little application since everything is managed at a domain level in the implementation rolled out locally, and uninstallation, service deactivation, and such is not possible unless you are on a list of Administrator users. If your at all concerned with users removing information via sneakernet, its not a bad solution. But all is not lost, for it is the ever possible realm of physical security that allowed me to circumvent this little application.

The chink in the armor here is the inability to prevent a user from booting from an alternative media, in this case, an external Windows Live CD I happen to have (I couldn’t possibly tell you which one it is unfortunately since I grabbed it from another individual, but I am under the assumption that it is BartPE, which would work well here). Once booted into the LiveCD, I simple “moved” the following 4 files:

C:\WinNt\System32\DLService.exe
C:\WinNt\System32\DlTray.exe
C:\WinNt\System32\DLTempAccess.dll
C:\WinNt\System32\DLService.rpt

That’s it. Once I rebooted, I had full access to my devices again. I still couldn’t uninstall, but oh well, for my purposes I was able to do what I needed.

Tuesday, September 05, 2006

Education: Body Worlds 3

One of the strangest things in recent years has been a phenomenon that I have found particularly odd. In just about every city I have visited, I have just missed a “Body Worlds” exhibit. It’s rather bizarre, since the concept intrigued me and I have wanted to go see it, yet I always just miss it. So, this weekend, I took a mini vacation and ventured up to Houston to see the last days of the “Body World 3” exhibit at the “Houston museum of Natural History”. It’s strange that I would want to see this exhibit so badly, considering my natural aversion to all things in the medical field, and by aversion I mean when I hear about medical stuff I get incredibly squeamish. Yet I was not disappointed with the exhibit. (Sorry for the lack of photos, cameras were prohibited in the exhibit).

Obtaining tickets proved to be a little more difficult than I anticipated. First, tickets sold out for most of the weekend. Now, for understanding, that was from Friday up until Monday, and the exhibit was running for a full 24 hours each day for the Labor Day weekend. Due to the overwhelming demand, I got tickets for 2:15 am on Sunday morning. That kind of added a dimension of creepiness to the whole trip. In fact, my travel companion decided that Michael Jackson’s “Thriller” was a perfect soundtrack for the adventure. I personally think he’s nuts…

The exhibit itself is incredibly educational. While the posed cadavers are the focus of media attention, the entire exhibit is actually much more in depth education into the components and functions of the human body. There were numerous glad cases with individual body parts, an explanation of the function of the body parts, and examples of body parts with various diseases. For example, in the Brain section, there is a “healthy” brain, and a brain with Alzheimer’s disease. In the section explaining the respiratory system, there is a health lung next to a smoker’s lung and a lung with cancer. It made me very thankful that I don’t smoke. And the full stretched nervous system and circulatory system were incredibly thought provoking, in the sense that you really begin to respect the complexity of these systems and feel an awe towards creation.

In regards to the posed cadavers themselves, I felt these were done in a very tasteful manner. It does give one a particular perspective on the fragility of life. One particular exhibit had the body of a man carrying his skin over his shoulder, with his muscular system exposed. The plaque that went with the exhibit described the purpose of the skin in the function of the human body and the muscular system. Another exhibit had the body of a man riding the cadaver of a horse. This was surreal as in the mans right hand was his brain, and the left hand was holding the horses brain. While shocking, the plaque explains the difference in sizes between the two brains, and compares the various body parts between the two creatures. Things that were exposed on the human were exposed on the horse as well. This showed an interesting similarity between the various functions, as well as the differences. There was a bit of entertainment with the exhibits as well. One exhibit showed a group of cadavers playing poker. I was a little amused that people were more fascinated by the hands the bodies were holding. But explanations of the brain functions, the eyes, and the muscles used to coordinate holding the cards legitimized the theme.

My overall impression was that I felt that trip was worthwhile. I typically get grossed out by this kind of stuff; however the exhibit did not phase me. There was a portion which was a bit of an advertisement for the Plastination Center that showed how some people were so inspired by the exhibit that they signed up to be volunteers post-mortum. While I was enlightened, and the concept of “immortality” is somewhat appealing, the exhibit still did not sway me that way. While there were those like me who were educated and respected the spirit of the exhibit, there were still a few who snickered at the exposed genitalia of the bodies. Some people are more mentally mature, I suppose, how strange that the children were better behaved than the adults. While this is a point of the controversy of the exhibit, the people who snickered are the same group that I feel should not be allowed to attend public education since they take it for granted.

If the exhibit comes around again, I will probably make it a point to see it.