2006-05-23

JBoss application configuration during deployment time

I was looking for a mechanism to configure my application during deployment time. We have our own configuration module but it does not handle the case for deployment descriptor. For example, the application needs to connect to different JMS queues in different environment. The JMS queue name is usually specified in the jboss.xml which is wrapped in the ear file. Our old approach is to use an Ant script build the ear file during the deployment time. It is tedious and also makes it difficult to see what is actually configured. Now I discovered a new way to do it. I can actually use ${param_name} inside the jboss.xml and use JBoss's system property service to define the actual value of the parameter in a seperate property-service.xml. By doing that, I can have the ear file created during the build time and deploy it along with an environment specific property file. It works just wonderful.
To know how it is done, you can take a look at org.jboss.metadata.MetaData.java. And here is the documentation from the Javadoc:

Go through the input string and replace any occurance of ${p} with the System.getProperty(p) value. If there is no such property p defined, then the ${p} reference will remain unchanged. If the property reference is of the form ${p:v} and there is no such property p, then the default value v will be returned. If the property reference is of the form ${p1,p2} or ${p1,p2:v} then the primary and the secondary properties will be tried in turn, before returning either the unchanged input, or the default value. The property ${/} is replaced with System.getProperty("file.separator") value and the property ${:} is replaced with System.getProperty("path.separator").

2006-05-08

Setting SSL on Apache 2.0

I have been working on Ajax and JSOD recently to prototype different ways to access a secure web service in a cross domain manner. As a result, I need to setup different SSL secure domains on my local box. I have used name based virtual host with Apache 2.0 before to setup mutiple hosts on my box. But setting SSL sites could be a challenge.

Fortunately I found a execllent guide on the web and was able to follow it through. By the end of the last week, I had three virtual SSL hosts setup on my local desktop. Here are some important configurations for it to work.

Listen 443
LoadModule ssl_module modules/mod_ssl.so

Include conf/ssl.conf

SSLMutex none
SSLRandomSeed startup builtin
SSLSessionCache none

#
# Use name-based virtual hosting.
#
NameVirtualHost *:443

SSLEngine On
SSLCertificateFile conf/ssl/tokentest.crt
SSLCertificateKeyFile conf/ssl/tokentest.key
DocumentRoot C:/tmp/sites/token.test.com
ServerName token.test.com
ErrorLog C:/tmp/sites/token.test.com/error.log
CustomLog C:/tmp/sites/token.test.com/access.log common

SSLEngine On
SSLCertificateFile conf/ssl/tokentest.crt
SSLCertificateKeyFile conf/ssl/tokentest.key
DocumentRoot C:/tmp/sites/www.test.com
ServerName www.test.com
ErrorLog C:/tmp/sites/www.test.com/error.log
CustomLog C:/tmp/sites/www.test.com/access.log common

SSLEngine On
SSLCertificateFile conf/ssl/tokentest.crt
SSLCertificateKeyFile conf/ssl/tokentest.key
DocumentRoot C:/tmp/sites/www.tokentest.com
ServerName www.tokentest.com
ErrorLog C:/tmp/sites/www.tokentest.com/error.log
CustomLog C:/tmp/sites/www.tokentest.com/access.log common

Implementing web service on JBoss 4.0.3

My recent project involves implementing web services on JBoss platform. The current implementation (4.0.3) is based on a modified Axis code base and does not provides a good support on top-down (starts from WSDL) development approach. I ended up have to integrate Axis 1.2 with JBoss 4.0.3. It works well so far.

At the same time, I have been looking into the next generation of JBoss WS which looks like very promising. During the course, I was also able to make some contribution to the new code base, fixing two defects (JBWS 754-755). I hope I will have more time in the future to make more contributions.

A maven 1.x plug-in for Agitar

I have been spending some time to create a Maven 1.x plug-in for Agitar . It was finally done last week and I have posted it on Agitar's forum .

2006-04-05

A Python script to sync. with Perforce

I have been using Fitnesse for a while. But I am not happy with the file system based backup. There is no easy way to setup a SCM back end for the Fitnesse. So I end up have to write a Python script to periodically sync. my local page with a Perforce depot. It works quite convenient.

Bitten by Maven

I have been struggling with my Maven script for a few hours. Finally it turned out is because of a property name I used. After I changed the name from "someA-someB" to "someA.someB", the problem is gone. Maven just does not like '-'.

2006-03-06

Turn off caching for Firefox

I have a need to turn off the caching for a particular page. I suppose the correct way is to set the cache directive in my Http request:

Cache-Control: no-cache

The server correctly returns the new version. The Firefox, however, insists to return me the cached old version.
So I have to turn the entire Firefox caching capability off through its about:config page
network.http.use-cache = false.

Too bad.

My first grease monkey script

I wrote my first grease monkey script today. It is to apply a XSLT style sheet on certain types of XML files. This is because usually the xml contains too much information and I just want ot view certain part of it and in more clean format such as a table. Here is the code:

/*
Title:
xml transformer

Description:
This is a Greasemonkey user script for Firefox.

This script applies XSLT on plain XML files
*/

// ==UserScript==
// @name xml transformer
// @namespace http://sample.com
// @description apply XSLT on plain XML
// @include http://localhost/*.xml
// ==/UserScript==

var contentType = 'text/html';
detectFormats();

function detectFormats() {
// define classNames and URLs of corresponding XSLT files - edit this to add new formats
var b = new Array();
b[0] = new Array('Root1', 'http://localhost/gmonkey/xslt/root1.xslt', 'text/html');
b[1] = new Array('Root2', 'http://localhost/gmonkey/xslt/root2.xslt', 'text/html');

// look for certain signature in document
for (i=0; i<b.len; i++) {
var schemaType = b[i][0];
if (document.getElementsByTagName(schemaType).length > 0){
xsltURL = b[i][1];
contentType = b[i][2];
GM_log(xsltURL);
fetchXSLT(xsltURL);
}
}
}

function fetchXSLT(url){
GM_xmlhttpRequest({
method: "GET",
url: url,
headers: {
'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
'Accept': 'application/atom+xml,application/xml,text/xml',
'Cache-Control': 'no-cache',
},
onload: function(response) {
var xslt = new DOMParser().parseFromString(response.responseText, "text/xml");
transformXML(xslt);
}
});
}

// transform the document to RDF using the XSLT file
function transformXML(xslt){
var processor = new XSLTProcessor();
processor.importStylesheet(xslt);
var dataXML = document;
var processed = processor.transformToDocument(dataXML);
var dataString = (new XMLSerializer()).serializeToString(processed);
location.href = 'data:' + contentType + ',' + dataString;
}

2006-02-17

Mapping xml to Python objects

I need to process XML in my python code and prefer to deal with the data in the pythonic way. From a wide options available, I choose to play with 3 of them.

* generateDS
*
gnosis.xml.objectify
*
amara

Because I am a fan of XmlBeans, it seems that generateDS should be my favorite choice. However, when I tried to run the code-generating script on my schema file, it encountered some recursive problem and complained:

.... parent.collectElementNames(elementNames) File "C:\Python\Lib\site-packages\generateDS.py", line 478, in collectElementNames parent.collectElementNames(elementNames) File "C:\Python\Lib\site-packages\generateDS.py", line 475, in collectElementNames base = self.getBase() RuntimeError: maximum recursion depth exceeded

The same schema file works fine with XMLBeans however.

gnosis.xml.objectify and amara are similar in that they don't require a schema file and will just turn any well-formed xml file into python objects. I had no problem parse the xml files with both modules. However, the gnosis.xml.objectify module seems not aware of the namespace prefix. Given following xml element:

<ns:book xmlns:ns="http://mycompany/bookproject">..

It generates an object called ns_book. It does not recognize : as a namespace prefix and just blindly translates it to _. I tried to search for a solution, but could not find anything.

The amara module, however, correct translates the object name to book and also keeps the namespace information. It seems that I will use this module at this time.

2006-02-16

2 tips about JBoss

* to retrieve the server name, such as "minimal", "default", "max", in your code

String serverName = System.getProperty( org.jboss.system.server.ServerConfig.SERVER_NAME); 


* to set an unique Xid for each JBoss instance

if you are running multiple JBoss instances on the same machine, you probably want to give each instance a unique Xid base name. To do it, you need to add
the following line in the jboss-service.xml:

<mbean code="org.jboss.tm.XidFactory" name="jboss:service=XidFactory">
<attribute name="BaseGlobalId">UniqueInstanceName</attribute>
<!--attribute name="Pad">true</attribute-->
</mbean>

2006-02-15

Successfully installed Twisted

I need to install the latest Twisted with Python 2.4. Unfortunately I don't have Visual Studio on my machine. Is there a way to do it. I find this excellent instruction on how to install Python extensions without VS. It works as advertised. I am happily running Twisted now!

2006-02-13

Upgrade to CruiseControl 2.4.0

Today I upgraded my CruiseControl to 2.4.0. The main reason is my project's SCM has been moved from Visual Source Safe to Perforce and I cannot get the CruiseControl 2.2.1 to work with Perforce. After the upgrade, everything comes back to normal.

2006-02-09

Python web service

In my previous post, I have discussed using Perl (SoapLite) as client to consume a web service implemented using Java(Apache Axis). I am curious to find how it will work out for other lanuages such as Python and Ruby. Here is my try for Python.

The tool I use is ZSI which stands for Zolera SOAP Infrastructure. It provides a tool wsdl2py that can generate Python stub code from a wsdl file. Run the tool on the wsdl file described in my previous post, I get two Python modules: MessageRouter_services_types.py and MessageRouter_services.py. With the help of these two modules, consuming a web service is straight forward. Here is the code:

def sendTo(destination, payload):

loc = MessageRoutingServiceLocator()
kw = { 'tracefile' : sys.stdout }
portType = loc.getMessageRouterPortType(**kw)

dest = ns1.DestinationType_Def()
dest._protocol = "http"
dest._category = "post"
dest._server = "127.0.0.1"
dest._port = 80
dest._target = "/router"

msg = ns1.MessageBodyType_Def()
msg._content = "payload"

req = RouteRequestWrapper()
req._destination = dest
req._request = msg

try:
response = portType.RouteMessage(req)._response._content
print "Response message is:\n %s" % response
except Exception, e:
response = repr(e)
print "Failed: ", e
raise e

2006-02-03

Programming languages I use now

Programming languages I use

Java - the language I use to earn my bread-and-butter and the one I will choose to do serious work

Ruby - it's style is cute I think, and it is fun to work with,

Python - as powerful as it's name suggests, I have been bitten by the white spaces, but now recovered

Perl - there is more than one way to do it, but many times I prefer there is only one way

2006-02-02

Complex test data setup in Fitnesse

I am creating functional specifications using Fitnesse for one of my systems. The system is a typical messaging system following the request/response pattern. Writing Fitnesse for this kind of system is straight forward and falls nicely into the BuildOperateCheck pattern. The only problem is that some types of requests are really really complex which could contain up to a few hundred different data fields. It is obviously not practical to setup all these fields in one table row using the ColumnFixture. I am thinking about how to deal with it.

One approach is to identify a small set of data fields that we will focus on testing on each page and create a fixture table that deals with only that small set of data. For the rest of the fields, we just assign some default values.

It is a reasonable assumption. Because on each Fitnesse page we usually test a specific requirement that most likely only involves several variables. For example, if I am designing a online book purchase system which is very complicated. But one of the requirement is to identify the tax rate. The tax rate are identified by the customer's postal code and county. Thus I only need to include "postal code" and "county" in my fixture table and I will be able to test that requirement.

In some rare cases, even that small set of data does not fit well in one table. In that situation, I managed to use multiple tables to collect the data for building a single request. It takes a little extra work but it makes the page more readable.

Fitnesse and coding convention

I have recently started to use Fitnesse to document the requirements of two of my projects. It is a wonderful tool that not only makes writing requirement more fun, but also makes the process works. Many of us are used to hundreds of pages of requirement document that only satisfies the "process police" but does little help to developers. The requirements written in Fitnesse is live and up to date. It just works!

But there is one thing I am scratching my head now which is the coding convention used by Fitnesse. For many fixtures, such as RowFixture, the convention is to match the column name to a public member variable in the fixture class. The traditional getter and setter approach is not supported here.

Many people oppose the idea of getter and setter and consider them evil. I am ok to either approaches. But the problem is the coding convention adopted by my organization is to have each member variable prefixed by the "m" and all member variables should be accessed through getters and setters. I cannot find a way to reconcile this problem. I won't be a problem to make a special case for this, but I really hope Fitnesse can support both approaches.

2006-01-31

Stomp

I have previous written about using web service as a mediator to let languages other than Java to communicate with JMS server. The approach is only for simple usage such as a testing tool. For more advanced usages, we can consider Stomp whcih is an open messaging protocol. There are various clients exist for Perl, Python and Ruby. On the server side, however, there is only ActiveMQ support.

2006-01-26

Make Perl speak JMS (3)

Finally I will implement a Perl web service client. The tool I pick is SOAP::Lite (v0.55).

SOAP::Lite comes with a handy tool similar to wsdl2java called stubmaker. Run our WSDL through the tool and it generates a Perl module MessageRoutingService.pm. This module wrapps the call to the single RouteMessage API defined in the WSDL. Following code shows how to use this module:
my @dest = ( 
SOAP::Data->name(protocol=>'jms'),
SOAP::Data->name(category=>'queue'),
SOAP::Data->name(server=>'my_jms_server'),
SOAP::Data->name(port=>'7222'),
SOAP::Data->name(target=>'my_queue'),
SOAP::Data->name(user=>'user_a'),
SOAP::Data->name(password=>'password_a') );

my @content = (SOAP::Data->name(content=>'Hello World'));
my @route_request = (SOAP::Data->name(destination=>\SOAP::Data->value(@dest)),
SOAP::Data->name(request=>\SOAP::Data->value(@content)));

my $result = MessageRoutingService->RouteMessage(@route_request);
print $result->{content};

Sounds easy? There is a small catch here. The generated stub module needs a little tweak. Somehow it misses the URI namespace for the top method wrapper element .
my %methods = (
RouteMessage => {
endpoint => 'http://localhost:8080/common_router',
soapaction => 'http://www.simple.code/router/action/RouteMessage',
uri => '',
parameters => [
SOAP::Data->new(name => 'document', type => '', attr => {}),
],
},
);

Without the URI, the method in the request looks like
 <RouteMessage xmlns="">

which is obviously wrong. After I manually add the URI in the stub to make it look like
my %methods = (
RouteMessage => {
endpoint => 'http://localhost:8080/common_router',
soapaction => 'http://www.simple.code/router/action/RouteMessage',
uri => 'http://www.simple.code/router/action/RouteMessage',
parameters => [
SOAP::Data->new(name => 'document', type => '', attr => {}),
],
},
);

Now the request becomes
<RouteMessage xmlns="http://www.simple.code/router/action/RouteMessage">

and it is happily accpted by the Java Axis server.

Make Perl speak JMS (2)

Now the WSDL is ready, I can start to implement the WS server in Java. The tool of choice is Apache Axis 1.2.1.

With the wsdl2java tool, it is straight forward to generate the codes from the WSDL file. Because we will only use the server code, so we only need to change one class HttpBindingImpl.java.
private MessageRouter mRouter = new MessageRouter();

public code.simple.www.router._2005._11._7.RouteResponseType routeMessage
(code.simple.www.router._2005._11._7.RouteRequestType document)
throws java.rmi.RemoteException
{
try
{
return mRouter.routeMessage(document);
}
catch(Exception ee)
{
throw new RemoteException("Failed to route the message", ee);
}
}

Here we just forward the request to the MessageRouter class and let it handle the details.

Axis provides several different ways to deploy the web services. The most convenient way is to use JWS file but it has certain limitations. Here I choose to use the WSDD file and embed the Axis engine in the Tomcat servlet container. As of how to integrate Axis with a servlet container, I could not find a good documentation. Eventually I figured it out and here is the code
protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException
{
PrintWriter pw = null;
try
{
pw = resp.getWriter();
resp.setContentType(CONTENT_TYPE);

String reqMsg = readContent(req.getReader());
mLogger.info("Got WS request:\n" + reqMsg);
MessageContext msgContext = new MessageContext(mAxisServer);
msgContext.setRequestMessage(new Message(reqMsg));
msgContext.setTargetService("MessageRouterPort");

String respMsgStr = null;
try
{
mAxisServer.invoke(msgContext);
respMsgStr = msgContext.getResponseMessage().getSOAPPartAsString();
}
catch (AxisFault fault)
{
Message respMsg = msgContext.getResponseMessage();
if (respMsg == null)
{
respMsg = new Message(fault);
((org.apache.axis.SOAPPart)respMsg.getSOAPPart()).
getMessage().setMessageContext(msgContext);
}
respMsgStr = respMsg.getSOAPPartAsString();
}

mLogger.info("Got WS response:\n" + respMsgStr);
pw.write(respMsgStr);
}
catch (Exception me)
{
mLogger.error(me);
throw new ServletException(me);
}
finally
{
if (pw != null)
{
pw.close();
}
}
}

public void init(ServletConfig cfg) throws ServletException
{
super.init(cfg);
EngineConfigurationFactory factory =
EngineConfigurationFactoryFinder.newFactory(cfg);
EngineConfiguration config = factory.getServerEngineConfig();
mAxisServer = new AxisServer(config);
}

The deployment descriptor for the servlet is:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app >
<servlet>
<servlet-name>MessageRoutingServlet</servlet-name>
<servlet-class>code.simple.MessageRoutingServlet</servlet-class>
<init-param>
<param-name>servletId</param-name>
<param-value>defaultServlet</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>MessageRoutingServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

Make Perl speak JMS (1)

To add JMS capability to Plum, I need to find a way to let Perl talk "JMS". Unfortunately there is no standard wire protocol for JMS. The JMS spec. defines the Java API but not the wire protocol. This is understandable as JMS is a Java centric system. Some JMS implementations, such as JBoss 3.x and 4.x, just use the Java serialization as their wire protocol. Others may use their propriate formats. This is an effort to define a generic XML protocol that can be utilized by different JMS implementations, but it is not there yet.

For now, a relatively simple way is to create a web service in Java which will route any requests to JMS server. Then one can make any lanaguage to speak JMS as long as they can consume the web service. Of course the limitation is that only text message will be supported.

Here is my try. I created a simple web service which is defined by following WSDL:
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="MessageRouter"
targetNamespace="http://www.simple.code/router.wsdl/2005/11/7"
xmlns:wsdlns="http://www.simple.code/router.wsdl/2005/11/7"
xmlns:router="http://www.simple.code/router/2005/11/7"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:stk="http://schemas.microsoft.com/soap-toolkit/wsdl-extension"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/">

<types>
<xsd:schema targetNamespace="http://www.simple.code/router/2005/11/7"
elementFormDefault="qualified">

<xsd:complexType name="DestinationType">
<xsd:sequence>
<xsd:element name="protocol" type="xsd:string"/>
<xsd:element name="category" type="xsd:string"/>
<xsd:element name="server" type="xsd:string"/>
<xsd:element name="port" type="xsd:long"/>
<xsd:element name="target" type="xsd:string"/>
<xsd:element name="user" type="xsd:string" minOccurs="0"/>
<xsd:element name="password" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="MessageHeadType">
<xsd:sequence>
<xsd:element name="header" type="xsd:string" maxOccurs="unbound"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="MessageBodyType">
<xsd:sequence>
<xsd:element name="content" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="RouteRequestType">
<xsd:sequence>
<xsd:element name="destination" type="router:DestinationType"/>
<xsd:element name="head" type="router:MessageHeadType" minOccurs="0"/>
<xsd:element name="request" type="router:MessageBodyType"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="RouteResponseType">
<xsd:sequence>
<xsd:element name="response" type="router:MessageBodyType"/>
</xsd:sequence>
</xsd:complexType>

<xsd:element name="RouteRequest" type="router:RouteRequestType"/>
<xsd:element name="RouteResponse" type="router:RouteResponseType"/>

</xsd:schema>
</types>

<message name="RouteRequest">
<part name="document" element="router:RouteRequest"/>
</message>

<message name="RouteResponse">
<part name="document" element="router:RouteResponse"/>
</message>

<portType name="MessageRouterPortType">
<operation name="RouteMessage">
<input message="wsdlns:RouteRequest" />
<output message="wsdlns:RouteResponse" />
</operation>
</portType>

<binding name="HttpBinding" type="wsdlns:MessageRouterPortType">
<stk:binding preferredEncoding="UTF-8" />
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="RouteMessage">
<soap:operation
soapAction="http://www.simple.code/router/action/RouteMessage"
style="document"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>

<service name="MessageRoutingService">
<port name="MessageRouterPort" binding="wsdlns:HttpBinding">
<soap:address
location="http://localhost:8080/common_router" />
</port>
</service>

</definitions>

2006-01-25

PLUM is ripe

My little tool PerL based Utility for Messaging is almost done. It needs to be improved in several places, but it is actually usable now. It has following functions:

* send single request to server using different protocols, currently supports HTTP(Post) and JMS(Topic and Queue)
* compare response message with expected response
* Perl RE can be used for matching XML type response
* send a batch of requests and compare responses with expected responses

The utility is a web based application. I am thinking about adding a comand line tool which is more suitable for batch processing.

Added login for the Catalyst application

I tried to add login for my testing utility which is a Catalyst based web application last night. I finished the code by carefully following the examples provided with document. Not too bad, just a couple of lines and I was done. But the application complained:
Can't locate object method "session" via package "myapp" 
I went back and reviewed the code several times and could not figure out what was wrong. After hours of struggle, changing configurations, switching to different plugin modules, I finally got the reason. In my code, I used following statement to import several Catalyst plugins:
use Catalyst qw/Static::Simple  Session  Session::State::Cookie  Session::Store::File/;
use Catalyst qw/Authentication Authentication::Store::Minimal Authentication::Credential::Password/;

It seems that the later import (authentication related) overrode the previous import(session related). Thus the session method is missing because the module Session is not imported.

To fix this, just change the code to:
use Catalyst qw/-Debug Static::Simple Session
Session::State::Cookie Session::Store::File
Authentication Authentication::Store::Minimal
Authentication::Credential::Password
/;

2006-01-23

SemanticXmlDiff failed on Unicode

I am using CPAN module SemanticXmlDiff to compare two set of XML files. It complains about not being able to handle "wide character". After looking at the source code, it turns out the problem is the MD5 function used inside the module. The MD5 hash is used to spead up the comparson of text node in the XML file. However, MD5 only supports computing hash of ASCII characters, thus it will fail on wide characters which has multiple bytes.

The solution? According to the Digest::Digest module, the text should be encoded before it is passed to MD5 function. To apply this, I change the following line from
$doc->{"$test_context"}->{TextChecksum} = md5_base64("$text");

to
 $doc->{"$test_context"}->{TextChecksum} = md5_base64(encode_utf8("$text"));

Now it works!

2006-01-19

Configure Catalyst 5.61 with Apache2 on Windows

I reconfigured my Catalyst app using mod_perl today. It was configured using CGI before as I was making a lot of changes and would like to page to always reflect the last change. The original configuration is:
<VirtualHost *:80>
ServerAdmin my@server.com
DocumentRoot c:/site/myapp/root
ServerName myserver2
ErrorLog c:/site/myapp/myapp-error.log
CustomLog c:/site/myapp/myapp-access.log common
ScriptAliasMatch ^/(.+) c:/site/myapp/script/myapp_cgi.pl/$1
Alias /static c:/site/myapp/root/static
<Location "/static">
SetHandler none
</Location>
</VirtualHost>

It works but the performance is horrorable. The new configuration is:
<VirtualHost *:80>
ServerAdmin my@server.com
DocumentRoot c:/site/myapp/root
ServerName myserver2
ErrorLog c:/site/myapp/myapp-error.log
CustomLog c:/site/myapp/myapp-access.log common
PerlOptions +Parent
PerlSwitches -IC:/site/myapp/lib
<Location />
SetHandler modperl
PerlHandler myapp
</Location>
<LocationMatch "/static">
SetHandler none
</LocationMatch>
</VirtualHost>

2006-01-18

Use DBD: :Oracle 1.16 to access Oracle 9.2 database

I try to connect to an Oracle 9.2 database using following code:
 my $dbh = DBI->connect($config->{dburl}, $config->{dbuser}, $config->{dbpass});

It failed with error:
DBI connect('host=host;sid=dbsid','user',...) failed: ORA-12705: invalid or unknown NLS parameter value specified (DBD ERROR: OCISessionBegin) at ..

I have to explicitly set the ENV variable NLS_LANG to make it work. The new code looks like:
$ENV{NLS_LANG}="AMERICAN_AMERICA.UTF8";
my $dbh = DBI->connect($config->{dburl}, $config->{dbuser}, $config->{dbpass});

and it works.

The initial thought is that the default NLS_LANG from DBD::Oracle has to match the server configuration which is "AMERICAN_AMERICA.UTF8". But the follwoing code works as well:
$ENV{NLS_LANG}="AMERICAN_AMERICA.US7ASCII";
my $dbh = DBI->connect($config->{dburl}, $config->{dbuser}, $config->{dbpass});

So maybe the DBD:Oracle is sending some invalid NLS_LANG string though I havn't figured out what it is.

The SimpleDateFormat class is NOT threadsafe

In researching a defect in my project, I find SimpleDateFormat class is not thread safe. The "bug" is reported back in JDK 1.2 and JavaDoc is added in JDK 1.5. I just don't see it. :(

2006-01-14

Perl object is just a blessed hash

To work on a small testing utility, I have to pick up perl again. Fortunately my memory still served me well until I got into this problem. How to trun a hash into object. After a little reading of my Learning Perl book. I figured out how it works. Once you understand perl object is just a blessed hash, then the solution is clear.

============================
package MyTest;

use strict;

my %hash = (color=>"red", shape=>"round");
my $hashref = \%hash;

sub not_typical_new {
my ($proto, $arg) = @_;
my $class = ref($proto) || $proto;
my $self = $arg;
bless ($self, $class);
return $self;
}

my $obj = mytest->not_typical_new($hashref);
print $obj->{color}, "\n";

Added a nice JSCalendar to the blogger

I added a nice JSCalendar to my blog. The instructions from ecmanaut helped. The first part is relatively easy. But the seconf part is difficult to follow. I ended up have to dig into the source code to find out how it works. After a hour or so, I finally figured out the basic functions. The challenge is because I used a customized template so that many CSS tags used by the scripts are not included. I have to change the template to include al l those CSS tags just to make the calendar work. I still cannot get the title hint to work as it requires more template change which I am not willing to spend more time. So I stopped here. But it is good enough for me now.

The del.icio.us tag script does not get along with Google's tools bar

I added the del.icio.us greasemonkey script so that I can create the bookmark after the post. However, it stopped working today. After a search on google, I learned that the script, for some reason, does not work with Google's toolbar extension. After remove the Google's toolbar, it works just fine. :(

2006-01-13

Added the freshtag to the blog

I added the freshtag to my blog today. It allows me to use del.icio.us tag to categorize my blog.