Thursday, September 27, 2007

IRC still alive and well

It had been a long time since I'd been on Internet Relay Chat (the real IM), so the other night I fired up Chatzilla and checked out a few channels like #jquery, ##java, ##linux and saw quite a bit of conversations taking place. If anyone reading this knows of good channels related to things like Java, general open source, portals, or enterprise development please drop a comment.

Wednesday, September 26, 2007

Commented out code...please use version control

I had to look through some code today to troubleshoot an issue. The code had tons of comments. You might say, wow that's a good thing right? Wrong, the comments were not comments about what the code was doing (or trying to do). Instead, the comments were commenting out old code dating back who knows how long. This makes the code so difficult to read and understand when the lines of old junk outnumbers the lines of code that is actually live and doing something.

There are so many great, free revision control systems available today (subversion, git, mercuial, bazaar), old code belongs in a revision control system and not in current code.

Tuesday, September 25, 2007

Dynamic Application Class loading in PeopleSoft

When Application Classes were first introduced in PeopleTools and they included interfaces, I knew that there had to be some way to dynamically load an application class. A lot of my recent work has been in the HR Recruiting module (Candidate Gateway) that delivers hundreds of classes.

Quite a few of the classes handle things like page layout and validation, things we definitely wanted to modify. They even delivered a lot of "factory" like classes that their code calls and all it does is return an object of a certain type. It seemed like a perfect setup for being able to customize the look and feel and behavior by simply supplying custom application classes. However, the implementation classes are hardcoded in the factory methods. So while there is a way to substitute the implementation of a certain class it still requires a modification to delivered code.

The Approvals Framework, on the other hand, is a perfect example of how application classes should be used. It is completely built around the concepts of dynamically loading classes at runtime based on values entered in setup tables. No code modifications are required, you just create your class and enter the fully qualified name in a setup table. The framework looks up the class you specify, loads it up dynamically at runtime and then calls the methods on it.

An example (code changed to protect the innocent):


class MyEventHandler extends BaseEventHandler
method onApprove();
method onDeny();
...
...
end-class;
...


The framework can then lookup the class name in a setup table and call the methods it provides. It knows what methods it can call based on the fact that my class must extend a class they know about (BaseEventHandler).


Local string &clazz = /* sql lookup here */;
Local BaseEventHandler &object;
&object = CreateObject(&clazz) as BaseEventHandler;
&object.onApprove();


PeopleSoft, which knows nothing about my custom class, is able to load it up and use it. This is a very powerful and elegant way to provide custom functionality without requiring anything more than an entry in a setup table. Unfortunately, I haven't yet seen many places where this type of extensibility is used in PeopleSoft 8.9.

Sunday, September 23, 2007

Notepad++ 4.3 released

Notepad++, my favorite text editor, released version 4.3 this week. Looks like mostly bug fixes and a few new features.

AJAX and PeopleSoft

After Ethan sent me the link to the PeopleSoft Auto-Save with Ajax post on the ITToolbox site I was inspired to try AJAX on a PeopleSoft page (and excited to try the jQuery library for the first time as it looked very cool). The blog post on the ITToolbox site had some great javascript code, but offered no details on how the whole thing was put together. Below are the exact steps I took to add AJAX to a page in PeopleSoft.

Before I get started I want to point out that I decided against trying the auto-save feature. While I think that this type of feature would be extremely useful in some use cases, I feel the Component you implement it with must be designed for it. Auto-saves should be saved as drafts and should not effect the current data stored in the tables. When the user actually clicks some sort of Save button, only then should the auto-saved data replace the data that existed before.

To simply my AJAX/PeopleSoft experience I decided to just create a dynamic DIV on an existing page. The DIV is updated periodically by sending an AJAX request to an IScript function which returns HTML that is placed directly into the DIV. This was done with PeopleTools 8.46.17.

Step 1.

I opened the Page I wanted to AJAX-ify and added an HTML Area control to the top of the page (specifically I added DERIVED_PTP.HTMLAREA).

Step 2.

I downloaded jQuery 1.2.1. I grabbed the "packed" version which is the smallest weighing in at only 26kb. As the ITToolbox entry hinted at, I tried adding the downloaded contents to an HTML object. However, when I tried to save the object in Application Designer I got the following error:

Object size exceeds 32K limit. Proceed with Save?

It let me save it anyway so I named the HTML object FR_JQUERY. When I opened up the page I received several javascript errors and when I looked at the cache javascript file that was created I found it was truncated. This happened when I used the code from the ITToolbox site:

<script src="%JavaScript(FR_JQUERY)"></script>


In order to make it work I added the contents of the packed jquery.js to a Message Catalog Entry (more details below).

Step 3.

I created an HTML object named FR_ISSUE_LOG_SRCH_DHTML with my code and a bind variable that I would later replace with the jQuery library. Here's the HTML object code:

FR_ISSUE_LOG_SRCH_DHTML HTML Object


<div id="new-issues">
</div>

%Bind(:1)

<script type="text/javascript">
var pollInterval = 15000;
function getIssues()
{
$("#new-issues").load("/psc/pshome/.../WEBLIB_..IScript_...");
setTimeout("getIssues()", pollInterval);
}

getIssues();

$("#new-issues").css({
left:"600px",
position: "absolute",
width: "250px",
fontSize: "9px",
padding: "2px",
});
</script>


Step 4.

On the Page Activate event I added the following code to populate the HTMLAREA field I had previously added to the Page.


Local string &jquery = MsgGetExplainText(31000, 3, "message not found");
DERIVED_PTP.HTMLAREA.Value = GetHTMLText(HTML.FR_ISSUE_LOG_SRCH_DHTML, &jquery);


What is noteable here is that the entire contents of the jquery.js file are stored in the Message Catalog and placed in the HTMLAREA using the %bind(:1) substitution variable. In our environment we can not easily added files to the webserver directory, so this provided a nice solution that did not require me to add any files to the webserver.

Step 5.

Finally, I created an IScript function that simply does a SQL Fetch for all rows entered in our custom table for the particular user viewing the page.

This was just an experiment on my part and only rates a 1 out of 10 on the coolness meter. My primary motivation was just to see how feasible it was to use AJAX on a PeopleSoft page. I was quite happy to find that it was easier than I expected (save for my little issue with placing the jquery.js code on the page). Also, I was able to dabble a lot with jQuery and find that it makes DOM manipulation and AJAX very easy and I will find a use for it in my next project.

Unlike the ITToolbox post, I was not attempting to interact with the component processor. Trying to involve actions by submitting the form information to the component processor and doing partial page updates is fairly complex and in my opinion looks like it can be prone to error. For the next couple of projects I think I'll stick to IScript type page refreshes to build my comfort level until a point when I am ready to do field validation and auto-saves.

Thursday, September 13, 2007

Just wanted to follow-up my previous post about Log4j in PeopleCode and post the actual log4j.properties file that I used. Using this file along with the code posted previously is all you would need to get started.


log4j.rootLogger=WARN, rolling

log4j.appender.screen=org.apache.log4j.ConsoleAppender
log4j.appender.screen.layout=org.apache.log4j.PatternLayout
log4j.appender.screen.layout.ConversionPattern=%d %-4r [%t] %-5p %c %x - %m%n

log4j.appender.rolling=org.apache.log4j.RollingFileAppender
log4j.appender.rolling.Threshold=DEBUG
log4j.appender.rolling.File=/some/directory/log4j.log
log4j.appender.rolling.MaxFileSize=20480KB
log4j.appender.rolling.MaxBackupIndex=5
log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
log4j.appender.rolling.layout.ConversionPattern=%d %p [%c{4}] %M %m%n

Friday, September 7, 2007

Log4j in PeopleCode

Jim's PeopleSoft Journal had a good entry on using Log4j in PeopleCode. This first article assumed that you would have control over the log4j.properties file in your environment. Jim also posted a follow-up on Log4j and Peoplecode Part II where he discussed how to programmatically configure the logging. This is nice for those, like me, that do not have access to modify the $PS_HOME/appserv/classes/log4j.properties file.

The programmatic approach works well, but I didn't want to hardcode all that information in each event I want to log information in. You can point log4j to an alternative logging file using the code below, that way you can keep control of the logging level and setup of the appenders in a file that is easy to modify. Of course, this assumes that in your environment you have some access to a directory where you can store the file and grant write access to it for the daemon that runs the application server.


Local JavaObject &log4jConfig = GetJavaClass("org.apache.log4j.PropertyConfigurator");

&log4jConfig.configure("/some/directory/log4j.properties");

Local JavaObject &log = GetJavaClass("org.apache.log4j.Logger").getLogger("edu.csufresno.cis.HRS_CE_RESUME.PageActivate");

&log.info("hi from peoplecode");


Note: I got tripped up initially because I didn't read Jim's code closely enough and tried using CreateJavaObject out of habit instead of GetJavaClass. Since both methods are static (i.e., can't create an instance of a Logger) it blew up with an error.

Pouring through logs

Been doing an inordinate amount of tracing and debugging lately. Our ERP system dumps a lot of junk to the logs. The following command is my friend.


tail -f trace.log |tee $HOME/mylog.log


I like to be able to follow the file as lines are added and also like to send the output to a log file that I can open in an editor so I can so more regex type searches.

Tuesday, September 4, 2007

Log4J - email notification of production errors

There are a couple of things I try to do before I sit down to write Java code for even the smallest project (cause they always grow). One is creating an ant build script and the other is creating a log4j configuration file and adding log4j to my library path. I used to sprinkle System.out.println's throughout my code and now I've gotten in the habit of using the logging library.

I hadn't used the SMTPAppender, but recent issues with production code got me thinking about it so I'd have a heads up before users starting yelling. Below is the log4j.properties file that's worked well so far. The code is all custom so I have total control of all log.error and log.fatal statements, so when I get an email I know exactly what the issues is. Wouldn't be possible if I still used System.out.


log4j.rootLogger=ERROR, SMTPAPPENDER, rolling

log4j.appender.screen=org.apache.log4j.ConsoleAppender
log4j.appender.screen.layout=org.apache.log4j.PatternLayout
log4j.appender.screen.layout.ConversionPattern=%d %-4r [%t] %-5p %c %x - %m%n

log4j.appender.rolling=org.apache.log4j.RollingFileAppender
log4j.appender.rolling.Threshold=DEBUG
log4j.appender.rolling.File=/tmp/csufresno-hrsrecruit-cnv.log
log4j.appender.rolling.MaxFileSize=20480KB
log4j.appender.rolling.MaxBackupIndex=5
log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
log4j.appender.rolling.layout.ConversionPattern=%d %p [%c{4}] %M %m%n

log4j.appender.SMTPAPPENDER.To=me@myaddress.com
log4j.appender.SMTPAPPENDER.layout=org.apache.log4j.PatternLayout
log4j.appender.SMTPAPPENDER=org.apache.log4j.net.SMTPAppender
log4j.appender.SMTPAPPENDER.Subject=[hrsrecruit] ERROR
log4j.appender.SMTPAPPENDER.BufferSize=512
log4j.appender.SMTPAPPENDER.From=myapp@myserver.com
log4j.appender.SMTPAPPENDER.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
log4j.appender.SMTPAPPENDER.SMTPHost=mysmtp.server.com

log4j.logger.edu.csufresno.cis=INFO



Just a note, took me a few runs to figure out that I needed mail.jar and activation.jar in my classpath. After that, smooth sailing.

Sunday, September 2, 2007

Vista, no thanks

In early August we received new laptops at work, some very nice dual core Dell Latitudes with a couple gigs of RAM. Now I can finally have JDeveloper and SQLDeveloper open at the same time. I have forgone installing Microsoft Office and instead am rolling with OpenOffice 2.2, no regrets. Still a Thunderbird fan, what's Outlook? The main Microsoft software I rely on is the OS, Windows XP.

In a year we are supposed to upgrade to Vista and MS Office 2007. Based on what I've seen of Vista so far I think that will be the time to move to Linux. Many in IT where I work have moved to MacOS and while I admire the BSD-like OS under the covers, I'd just be trading one corporate vendor for another. In addition, most of the software that I need to run in order to do my job is either not available on the Mac or is in beta. There is not one tool I use that requires MacOS. There are quite a few tools I use that are available for both Windows and Linux. For those cases where a Linux version is not available I can use Wine or an XP guest OS in VMWare Server.

So I have one year to decide what distro to run. I've been running Ubuntu on my home laptop for about the last 8 months. It's a nice distribution, very easy to setup and use. It has given me a great appreciation for the Debian packaging system. I have it narrowed down to either a Debian or Redhat distribution. Redhat would be a good choice because it'll likely replace our Sun Solaris boxes in the near future. I've been working with CentOS in a VMWare image for a little while and found I really like both the workstation and server installs. As for a Debian distro I think I would go with plain old Debian. While Ubuntu does have a large user base, I need very few of the features that are Ubuntu specific. I dislike some the configuration changes I've seen lately in Ubuntu (i.e., their choice of replacing the inittab with event.d). I want my Linux system to be as standard as possible if I go with Debian and I think Ubuntu is starting to stray away from Debian a little too much and I don't want to end up running a distro that forks from Debian.

I think the main decision will depend heavily on package management, I know Debian's apt-get is excellent. For now I need to continue playing with CentOS to see how Yum stacks up.

Podcasts

I am trying to make up for lost time since I haven't posted in a long, long, long time. Here my list of podcasts that I regularly listen to....

  • bsdtalk
  • Casting from the Server Room
  • FLOSS Weekly
  • Google Developer Podcast
  • In the Trenches
  • The Java Posse
  • Linux Reality
  • OnSoftware
  • Oracle TechCasts
  • Perlcast
  • Software Engineering Radio
  • SourceTrunk

iText - Java PDF library

I recently had to do some tuning on a project I had done that involves converting and merging documents into PDF format. The main performance issue turned out to be poor performing sql and a function-based index fixed it. I decided to try tuning a few other pieces of code and that brought me to try an newer version of the iText library...wow was I pleasantly surprised.

In the project I use iText to merge PDF documents into a single document and also use it to convert TIF and JPG files to PDF format. The ERP system this project was going to be a part of shipped with iText 1.02 so that is why I initially decided to use that library for the PDF manipulation. As part of my tuning effort I decided to try the latest version, 2.0.4. On average performance improved over 40%. As an added bonus some TIF files that failed to convert in the prior 1.02 version were handled flawlessly by the newer release.

In the near future I would like to compare the performance against other Java PDF libraries (such as PDFBox) to see how iText compares. The code that handles the PDF manipulation is isolated from the rest of the logic and implements an interface so it shouldn't be too difficult to try the test since the implementing class is plugable.

Couple lessons learned for me are:
  1. Write classes that do one thing and do it really well. I was lucky here, my project is relatively small and the code was well isolated making the upgrade in versions easy to test side by side.
  2. For sourceforge projects that I rely on I will sign up for the release mailing list and try out new versions. Open source projects like iText are continually evolving. I made a huge mistake of thinking that the art of PDF manipulation would not have advanced much since the release of 1.02 (which I think was sometime in 2004).
  3. iText is a great PDF library for Java. I had never used this library before this project but had heard a lot about it. What really impresses me the most about the project is the documentation and the fantastic examples on the site. I was able to use a lot of code from the examples and that left me to focus more on my program's logic rather than on the details of merging PDF files or converting images to PDF.
One last thing I want to mention was how painless the transition was from release 1.02 (circa 2004) to the latest release of 2.0.4. Just had to change 2 method calls from deprecated methods to new ones that were direct replacements. Great library, fast, stable API, well documented, definitely recommend it.