Wednesday, November 19, 2008

Revisited: Spring InvervalJobs and scheduling in Liferay 5

Update to a better way to go about Spring scheduling: I tried simply defining a destroy method on the scheduler bean instead of relying on the extended ContextLoaderListener. This never appeared to be invoked and subsequently didn't unschedule the jobs. This is quite problematic in the long-run. Not good to have duplicate jobs running at the same time, it's sloppy and bad things could happen. So simply having the destroy method was insufficient.

Instead of extending Liferay's JobScheduler (as I suggested in the previous post), it made more sense to create a singleton POJO that then invokes com.liferay.portal.kernel.job.JobSchedulerUtil.getJobScheduler().schedule(). That way the dependency is relying on Liferay's Quartz integration altogether (not thinking any of this would ever live outside of Liferay, but in the event that it does should be able to propagate that dependency with minor adjustments). Everything said and done (and tested), this is the scheduler:


public class JobScheduler {
private static Log _log = LogFactory.getLog(JobScheduler.class);
public Set jobs = new HashSet();

/**
* Set all of the scheduled jobs.
*
* @param jobs Set of jobs to schedule.
*/
public void setJobs(Set jobs) {
this.jobs = jobs;
}

public void init(){
com.liferay.portal.kernel.job.JobScheduler scheduler = JobSchedulerUtil.getJobScheduler();

for(IntervalJob job : jobs){
try {
_log.info("Initializating " + job);
scheduler.schedule(job);
}
catch (Exception e) {
_log.error("Initialization error instantiating " +job);
_log.error(e);
}
}
}

public void destroy() {
com.liferay.portal.kernel.job.JobScheduler scheduler = JobSchedulerUtil.getJobScheduler();

for(IntervalJob job : jobs){
try {
_log.info("Unscheduling " + job);
scheduler.unschedule(job);
}
catch (Exception e) {
_log.error("Unscheduling error with" +job);
_log.error(e);
}
}
}
}

Spring InvervalJobs and scheduling in Liferay 5

If you have a Liferay portlet that requires some scheduling you can easily use Liferay's built-in Scheduler to add an IntervalJob to the job list, like this. However, what if your IntervalJob is a Spring bean and has dependencies on other Spring beans in the portlet? Unfortunately, at the time of this writing (Liferay 5.1.2), the hot deploy code invokes the Scheduler configuration and execution before the context is initialized--which means you're up a creek when Spring is setup to be initialized with the context (happens to be my case).

An alternative approach is to extend com.liferay.portal.job.JobSchedulerImpl with a Spring singleton and configure the jobs via Spring. While this is very flexible, the singleton is now operating outside the Liferay Quartz realm and therefore will not be subject to the lifecycle of the portlet. That is to say, when you redeploy the portlet the jobs stay scheduled. A more annoying aspect to this is that if you try to shutdown Liferay it appears to hang. Sure the log says that Coyote is stopped, but that's not the case and the process appears to be waiting on a thread. This in turn requires manually killing every time. During development this is such a pain. My guess, without significant research into the bowels of the Liferay Quartz integration, is that the Spring singleton hasn't been properly disposed of.

One solution to this situation is to extend org.springframework.web.context.ContextLoaderListener with something like this:


public class SpringSchedulerContextLoaderListener extends org.springframework.web.context.ContextLoaderListener{
private static final Logger logger = Logger.getLogger(SpringSchedulerContextLoaderListener.class);
public void contextInitialized(ServletContextEvent event) {
super.contextInitialized(event);
}

public void contextDestroyed(ServletContextEvent event) {

JobScheduler j = (JobScheduler) StaticApplicationContextHolder.getApplicationContext().getBean("jobScheduler");
j.shutdown();

super.contextDestroyed(event);
}
}



This will ensure that the Spring singleton JobScheduler will unschedule the registered IntervalJobs when the context is destroyed. You're good to go once this entry replaces org.springframework.web.context.ContextLoaderListener in web.xml.

There may be a more efficient way to do this, but for now this works.

Monday, March 31, 2008

Alfresco content management with Liferay

At work we've been pondering a better long-term solution to our content management. A year ago we gave some thought to Liferay and in fact deployed it for one of our sites. Recently we've reconsidered our approach due to some new additional requirements accompanied with a more thoughtful perspective on leveraging internal content management with future web projects. We spent last week looking at JSR-170 alternatives/companions to Liferay, which with all due respect, only provides the implementation in their Document Library. We wanted something all-encompassing, that is, a repository where we could manage both web, print and other electronic content. Alfresco seemed to be the best option for us. After all of us spending a week with it and having various meetings trying to define the roles of stakeholders and functionality requirements, this was my conclusive perspective with enumerated priorities:

  1. Versioning
    This is easily satisfied with Alfresco and I was specifically impressed with the various ways to update content. I'm particularly pleased with the multiple capabilities of creating/updating content given the built-in CIFS server, FTP server, Office plug-ins and web interface. This wide array of interfaces should enable our users to begin versioning content with a limited learning curve (especially in terms of the shared drive notion). The WebProject versioning feature is very worthwhile in that it provides us with the ability to view/rollback content at any given time for each release, very helpful for auditing and liability. Lastly, their implementation of sandboxing is especially beneficial in concurrent development as each user can submit their work to workflow after sufficient authoring and testing.

  2. Document Management
    Alfresco was written primarily to manage documents, and given the aforementioned information on versioning, I think it's very capable for our needs.

  3. Integration
    I'm very pleased and excited about the ease of creating REST endpoints using Alfresco's WebScript framework. We won't have to write any extra functionality (read: additional JARs) to work with existing APIs but can rely on implementing custom WebScripts for exposing what we want, how we want. This is particularly useful for rapid development for any of our potential integration points, this is specificall a boon for both integration with our Rails CRM and the custom Liferay content portlet Jeff Wilson is writing.

  4. Workflow
    I think creating workflows specific to our needs will require the most work. Granted, the WCM component ships with a very basic approval workflow, we'll still need to create custom workflows once we decide how to hone our processes (and choose our deployment strategies). Depending on our needs, we may only need to define the rules in XML and forgo additional code (I believe our definitions will need to precede the investigation of additional functionality).

  5. User Experience
    Again, referencing the Versioning info above, I think this is covered. It'd be very helpful for the users to see that state/phase of workflow that a given item is in, but that appears to be a current enhancement request (per Jared).

Additional Benefits
  • Search: all meta-data (including custom aspects) is indexed, with incredible ease users will be able to find content much faster than perusing through shared drives trying to remember the location of specific files.


  • Task dashboard: users are able to see what tasks they have awaiting their action (be it approval, updates, reviews, etc.)


  • SSO options are plentiful for integrating with our ActiveDirectory: LDAP, NTLM, Kerberos


  • Simplified replication: there's already a pre-configured XML doc for repository replication


  • Space Rules: Alfresco has a great rule-engine for manipulating content based on a set of Space rules. For example, specific meta-data (via custom aspects) can be applied to certain content as defined in the rules. Space rules have an inheritance model

  • Roles are configured per Space (and thus also subject to inheritance) enabling a very flexible detailed system of privileges. Roles can be applied to users or groups of users, per Space.

  • Content transformations: Alfresco integrates with OpenOffice to provide instant content transformations(text to PDF, PowerPoint to Flash) and can be extended to provide custom transformations.


  • Send content to Alfresco via email: The next release of Alfresco will include the ability to add content to Alfresco via email attachment. This could be a very efficient way for sales people to put quotes,proposals,contracts, etc straight into Alfresco without leaving their email client.

  • Space Templates: we can setup a space and template it to create future spaces based on that template, thereby ensuring default layouts and content are appropriately propagated.


  • Alfresco deployable run-time enables us to deploy the repository to our environments w/o the overhead and deployment of the web client (a clear separation of concerns strategy that also avoids potential content tampering).

  • Stability and product maturation: Alfresco is clearly a player in the marketplace with 400+ enterprise clients and 20k deployed instances.


  • Speed: Alfresco and RedHat created a JSR-170 benchmark with Optaros validating its results in a 10 million doc test exercising repository corruption avoidance and high-concurrency usage, 0.4s response time. Updated results.


  • Search: all meta-data (including custom aspects) is indexed, with incredible ease users will be able to find content much faster than perusing through shared drives trying to remember the location of specific files.



I clearly believe that the Alfresco solution, coupled with our Liferay content-rendering portlet, is the best approach we could pursue in managing long-term corporate content. It enables all of our departments and users to create and manage content, whether print or web-related, in a variety of very intuitive and thoughtful interfaces. Furthermore, it satisfies multiple IT goals in terms of application integration, data replication, content authorization and workflow/process definition. To that end, and knowing more functional valued enhancements will be soon released, I strongly recommend it.

Been a pleasure to muck with it, looking forward to future implementation (which I hope is approved).

Thursday, March 20, 2008

Great look at virtualization

Virtualization: Nuts and Bolts

What I appreciated most about this article was the lack of fluff found in most of the VMWare or XEN docs comparing X or Y and why they're better than the other guy. Johan does a great job of providing a bit of history and background in virtualization (specifically binary translation then paravirtualization) and then explores the Intel VT-x and AMD SVM roles at the hardware level. He discusses memory and I/O challenges that can still be hindrances. It's a long article so if you're not interested in all the gory details, at least check out page 12 for a good look at benchmarking (and what's NOT being benchmarked) and page 13 for a well-summarized conclusion.

Coming away from reading this leaves me anxious for the next article and future enhancements at the hardware level. I'd like to find more articles similar to this one for more information and academic research. Appears there are still great strides to be made to hone efficiency. Fun stuff!

Tuesday, December 18, 2007

JNI library testing on OSX 10.5

Refactoring a bunch of the JNI H-ITT code that I was working on last month caused me grief when I was nailed with the presence of UnsatisfiedLinkError during my maven build. My Mac prototype code was very simplistic and I had all the libraries (and Java test driver) in the same directory and it worked flawlessly. Sheesh, LD_LIBRARY_PATH in OSX 10.5 is not the right env variable, should be DYLD_LIBRARY_PATH. Once that was added to my pom I was good to go, tests passed as expected.

Thursday, December 13, 2007

Madison Summit Oxfords, second pair

Three years ago I wrote about a horrible experience with junk shoes from Wal-Mart . Tonight I bought my second pair of Timberland Madison Summit Oxfords. I love these shoes and forwarded my thoughts to Timberland:

To Whom it May Concern,

I just wanted to write a quick note and thank you for such fantastic shoes. Two years ago I was looking for a new pair of shoes that I could use for work (business-casual), for school (walking around college campus), for home (playing sports with my kids, shoveling the walk, and wearing around the house) and for anything else. I was talking with a fellow in my church congregation and he suggested Timberland. I headed over to a local sporting good store and the sales guy immediately showed me your Madison Summit Oxford.

I purchased that pair of shoes 23.5 months ago and they has served me very, very well. I have worn them exclusively, for every activity. The best part about this shoe was how well it "restored" when I regularly applied my leather-weather cream.

Tonight I purchased my new pair, the exact same size and model for $30 cheaper than two years ago. I commend you on a fine product and its outstanding endurance. I appreciate your company's values and commitment to the environment. I hope this next pair will last me another two years and that I can continue on with my Madison Summit Oxford addiction in the foreseeable future (2010, 2012, 2014...).

Wednesday, December 12, 2007

TextMate: Subversion Annotate command

I really like vc-annotate in emacs. Considering the other very easy command I created earlier, thought I'd give this one a shot too (since it's not in the default Subversion bundle). Again, extremely simple and minutes to complete. Here's the command (edited from the Info command):

require_cmd "${TM_SVN:=svn}"
: ${TM_RUBY:=ruby}
FORMAT_INFO="${TM_BUNDLE_SUPPORT}/format_annotate.rb"

"$TM_SVN" annotate "$TM_FILEPATH" |"$TM_RUBY" -- "$FORMAT_INFO"




And here's the Ruby formatter:

require ENV['TM_BUNDLE_SUPPORT']+'/svn_helper.rb'
include SVNHelper

puts html_head(:window_title => "Info", :page_title => "SVN Annotation", :sub_title => 'Subversion')
puts '<div class="subversion">'
STDOUT.flush
@colors = ['BlanchedAlmond',
'BlueViolet',
'Brown',
'BurlyWood',
'CadetBlue',
'Chartreuse',
'Chocolate',
'Coral',
'CornflowerBlue',
'Crimson',
'Cyan',
'DarkBlue',
'DarkCyan',
'DarkGoldenRod',
'DarkGray',
'DarkGreen',
'DarkKhaki',
'DarkMagenta',
'DarkOliveGreen',
'Darkorange',
'DarkOrchid',
'DarkRed',
'DarkSalmon',
'DarkSeaGreen'] #see http://www.w3schools.com/html/html_colornames.asp for more pretty colors

@color_hash = Hash.new
@color_ind_size = @colors.size() -1

def color_for_rev(rev)
color = @color_hash[rev]

unless (color)
color_index = rev % @color_ind_size
color = @colors[color_index]

@color_hash[rev] = color
end

color
end

$stdin.each_line do |line|
rev = line.strip.split(" ").first.to_i

color = color_for_rev(rev)
colored = "<div style='color:#{color}'>#{htmlize(line.strip)}"

puts(colored)
end

puts("</div>")
html_footer()


TextMate command db query prompt

I decided to give TextMate another whirl last night. I thought since I gave NetBeans 6 some time recently I'd see how efficient a couple of days with TextMate would be (compared with my life-blood emacs). I was pleasantly surprised at its extendability, speed and myriad of bundle choices. While perusing the SQL bundle I noticed I couldn't see of a way of directly typing in a query. I really like sql-mysql in emacs, so I was hoping I could do something similar--the workaround being typing a query into a buffer, selecting it, then invoking the command to send it. After poking around in the TextMate manual I was shocked at how easy it appeared to be to add custom commands. In less than a minute I had this (edited straight from the manual example of showing a dialog for input):

res=$(CocoaDialog inputbox --title "Send query" \
--informative-text "Enter query text:" \
--button1 "Submit" --button2 "Cancel")

[[ $(head -n1 <<<"$res") == "2" ]] && exit_discard res=$(tail -n1 <<<"$res") db_browser.rb --query="$(tr '\n' ' ' <<< "$res")"


If I can just get over my remaining habits (screen splitting, hippie-expand, and more), I may end up paying for this editor. Too bad it's not OSS :(

Wednesday, November 21, 2007

Rxtx to MINA H-ITT Flash integration out of the park

That's a lame title, but it advertises all of the relevant (okay, short of Java's role, which is inherent since Apache MINA is written in Java) technologies in my latest project at work. We'll be using a Flash based application for our next training platform. One of the goals of the platform is to integrate an audience response system with the training. We specifically chose H-ITT as our provider because the offer a simple SDK (cross-platform translation library) and they appeared to have the most developed system and provide solid support.

Today I finished a proof of concept and made it available to them, here are the relevant sections of my email (heading home soon, this saves me time from repeating myself):
--begin email chunk--
I wanted to let you know that I have successfully completed a workable Windows H-ITT-Java-Flash prototype. I have some of the code operational in OSX but haven't pursued it in light of our goal of a single Windows deliverable (with embedded JRE) that _doesn't_ require an installation routine. I've used Java as the communication layer, natively interfacing through your SDK, and created a rudimentary TCP protocol for communication with the Flash application. I've heavily relied on the open-source Apache MINA project in conjunction with the Rxtx libraries (which will be included in a later stable release of MINA), Simple-Log and Launch4j.

The prototype simply exhibits the ability to auto-detect the transceiver's serial port (baud can be manually configured via a properties file, defaults at 19200), sets up the appropriate serial connection, and then forwards the responses to the Flash app. Java logging is written to disk unless executed from a read-only medium. The Flash prototype provides an "Acquire" button that kicks off the auto-detect, a log console (scrolling is there, but not so visible, I'm not a Flash guru), and also virtual buttons related to the remote. Thus, when configuration is successfully complete, pressing the buttons on the remote invokes the related event in the Flash app to show which button was pressed. The whole setup serves as a basic proof-of-concept for what we're pursuing with our next-gen training platform.
--end email chunk--

Thanksgiving is tomorrow, I'm feeling grateful: So I've already plugged H-ITT in the beginning of the post, but I really gotta say they've been great to work with and very prompt in answering questions. Next plug goes to the MINA team. MINA intuitively and simply (relatively, I thought the docs and examples were sufficient) provided a great framework for custom socket communication with Flash. It's elegantly architected (IMHO) and easily integrated with what I had envisioned for the Java communication layer. Props to Mike Heath for recommending MINA and assisting my approach. Simple-log and Launch4j simplified logging and customizing a single Windows .exe file. Lastly, Rxtx provided the crux of the serial-communication and I applaud the development team in their cross-platform approach and deliverables. Finally, my thanks to Ross Asay for the JNI help, since I was intending a cross-platform delivery to correspond with H-ITT's libraries this proved to be a good challenge and learning experience.

Wednesday, September 12, 2007

Failed to get IPC connection

This convenient message showed up tonight on a Windows 2003 Server VM under VMWare server. Restarting the service, no go. Restarting the VM, no go (same message). Remove devices and change the VM settings, no go. Restarting host (Suse), no go.

A bit of looking around didn't help much, but someone mentioned a permissions issue. Here's the log entry:
Sep 12 18:13:56: vmx| CnxAcceptConnection: Could not receive fd on 187: invalid control message
Sep 12 18:13:56: vmx| Failed to get IPC connection


Going out to the directory of the VM, and executing a "chown root:root -R ." did the job. Restarting the VM after that brought it up nice and happy. So the question remains as to what caused this to occur.

Wednesday, August 15, 2007

Why I love Apache's Ldap Studio

I've gotta modify some attributes on people in our ActiveDirectory. The fun Microsoft way to do this is to download ADAM-adsi management console plugin, and then go from there. It's pretty basic and is usual MS ugliness. On the other hand, Apache Ldap Studio provides a much better user experience. Being built on top of Eclipse enables a slick interface and a very intuitive way of dealing with your directory (browsing, searching, editing). Kudos to the guys that wrote it, I can now enjoy mucking with AD from my Mac with ease and finesse.

Thursday, August 09, 2007

Remote files in Emacs

Today I decided I was sick of always SSHing everywhere only to open up a couple of files for editing and then saving them. It doesn't make sense to copy my .emacs everywhere either. I discovered Tramp, and it just makes me all the more happy with my Emacs zealotry. Even better, my current snapshot already includes it.

Works great with dired too:
/ssh:hoser@somebox:/whateverdirectory

Wednesday, August 01, 2007

installing the latest nginx on OS X

I've read a lot about nginx lately and wanted to test its performance for our up-and-coming Ruby on Rails CRM application. It looked very easy to setup and easily configurable (especially with this). My first attempt was installing it through MacPorts, but that didn't fly, as I received the unpleasant "dyld: Library not loaded: /usr/local/lib/libpcre.0.dylib". Turns out I had the same problem when trying to execute after building straight from source (which happened to be a newer version).

So with MacPorts I installed pcre 7.2_0, deactivated 7.0_0, created the symlink as "sudo ln -s /opt/local/lib/libpcre.0.dylib /usr/local/lib/" and was then able to start up nginx from the source build. Unfortunately the generated configuration file I am using expected a user and group for "nginx". A quick work around for that was to just reference myself instead. Here's what I ended up with (awaiting tweaks and optimizations):


#user and group to run as
user russ russ;

# number of nginx workers
worker_processes 2;

# pid of nginx master process
pid logs/nginx.pid;

# Number of worker connections. 1024 is a good default
events {
worker_connections 1024;
}

# start the http module where we config http access.
http {
# pull in mime-types. You can break out your config
# into as many include's as you want to make it cleaner
include conf/mime.types;

# set a default type for the rare situation that
# nothing matches from the mimie-type include
default_type application/octet-stream;

# configure log format
log_format main '$remote_addr - $remote_user [$time_local] $status '
'"$request" $body_bytes_sent "$http_referer" '
'"$http_user_agent" "http_x_forwarded_for"';

# main access log
access_log logs/access.log main;

# main error log
error_log logs/error.log debug;
#error_log logs/error.log debug_http;

# no sendfile on OSX
sendfile on;

# These are good default values.
tcp_nopush on;
tcp_nodelay off;
# output compression saves bandwidth
gzip on;
gzip_http_version 1.0;
gzip_comp_level 2;
gzip_proxied any;
gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml
application/xml+rss text/javascript;

# this is where you define your mongrel clusters.
# you need one of these blocks for each cluster
# and each one needs its own name to refer to it later.
upstream vscrm {
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
}

# the server directive is nginx's virtual host directive.
server {
# port to listen on. Can also be set to an IP:PORT
listen 80;

# sets the domain[s] that this vhost server requests for
server_name vscrm;

# doc root
root /home/russ/forge/svn/vscrm/trunk/public;

# vhost specific access log
access_log logs/vscrm.access.log main;

#Set the max size for file uploads to 50Mb
client_max_body_size 50M;

# this rewrites all the requests to the maintenance.html
# page if it exists in the doc root. This is for capistrano's
# disable web task
if (-f $document_root/maintenance.html){
rewrite ^(.*)$ /maintenance.html last;
break;
}

if ($host ~* "www") {
rewrite ^(.*)$ http://vscrm$1 redirect;
break;
}

location / {


# needed to forward user's IP address to rails
proxy_set_header X-Real-IP $remote_addr;

# needed for HTTPS
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect false;
proxy_max_temp_file_size 0;

# check for index.html for directory index
# if its there on the filesystem then rewite
# the url to add /index.html to the end of it
# and then break to send it to the next config rules.
if (-f $request_filename/index.html) {
rewrite (.*) $1/index.html break;
}

# this is the meat of the rails page caching config
# it adds .html to the end of the url and then checks
# the filesystem for that file. If it exists, then we
# rewite the url to have explicit .html on the end
# and then send it on its way to the next config rule.
# if there is no file on the fs then it sets all the
# necessary headers and proxies to our upstream mongrels
if (-f $request_filename.html) {
rewrite (.*) $1.html break;
}

if (!-f $request_filename) {
proxy_pass http://vscrm;
break;
}
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

Tuesday, July 31, 2007

sanitizing Rails input parameters

I really like how Tapestry automagically escapes HTML input when posted from a form. In fact, it was just great never having to worry about that when coding. I'd like to have the same functionality in Rails especially after reading about Rails XSS vulnerabilities and work-arounds. Since the webapp I'm writing has no requirements for allowing formatted user input, I just need something simple to clean/sanitize all the params. Here's the latest:

#escapes all HTML from the given hash's values (recursively applied as needed)
def sanitize(hash)
dirty_hash = hash

dirty_hash.keys.each do |key|
value = dirty_hash[key]

if(value.kind_of?Hash)
dirty_hash[key] = sanitize(value)
else
if (value && value.kind_of?(String))
dirty_hash[key] = CGI.escapeHTML(value)
end
end
end

hash = dirty_hash
end

This is then invoked by a before_filter. Seems to do the job, is there a better/cleaner/faster way of doing this? Let me know how it could be improved...

Wednesday, July 11, 2007

Ruby: constant time for include?

Sure would be nice if the Ruby docs (including this book) would provide more details to the implementation of the Set class. According to this, Set implements its backing collection with a Hash, which would essentially mean that it's synonymous (to some degree) with Java's HashSet. Thus providing a constant time lookup when Set#include? is invoked. Just for grins I benchmarked this in irb with a million Fixnums and was pleased with the 15 microsecond lookups. I looked at the source of both and found a fair amount of similarity.

Friday, June 22, 2007

Sorting serialized objects from a YAML file, Ruby vs Java

I recently had a large dataset that I needed to sort, basically a bunch of objects with 9 string attributes. I dumped them to a YAML file so I could benchmark various aspects of sorting (Class#to_yaml really rocks, really). As it turns out Enumerable#sort_by was the more efficient way to go rather than Enumerable#sort (check it). The dataset contained 5429 unique objects, here's the benchmarking I did in irb:

>> bm do |x|
?> x.report("all"){results.sort{|a,b|a.account_name <=> b.account_name}}
>> end
user system total real
all 0.070000 0.000000 0.070000 ( 0.073835)
>> bm do |x|
?> x.report("all"){results.sort_by{|a|a.account_name}}
>> end
user system total real
all 0.020000 0.000000 0.020000 ( 0.020745)


Noticeable difference between the two methods, and quite pleasing to see how fast sort_by performed (Apple MBP 2.33GHZ 2GB RAM Ruby 1.85). Then the thought occurred to me, since I have this in a YAML file, I could write something really quick in Java and see how fast the sort would be by dumping it into a TreeSet.

So I set off to find out how I could marshal the data into POJOs from the YAML file. JYaml and JvYaml are the only (insofar as I looked) open source Java YAML libraries. Both seem half-baked in their own right, likely containing just the functionality the respective author needed and not much more (at least that's how it appeared). I ended up using JvYaml and had to search-replace the yaml entry identifier("tag:yaml.org,2002:map") so that JvYaml would create HashMap instances for me instead of its completely worthless PrivateType class.

From there I iterated through the maps to create the POJOs and shoved them into a HashSet. Once that was completed I created the comparator for my POJO, passed it into the TreeSet constructor and then timed an addAll giving it the entire HashSet:
#1: 55ms
#2: 28ms
#3: 27ms (pretty much constant thereafter)

Interesting results eh? The dataset I used was from a randomized generator and now that I have these initial numbers ( Ruby appears to have won this round), I want to test a larger set. And to be really fair I should do more research on benchmarking best practices (in addition to still needing to take my stats class).

Tuesday, April 17, 2007

X11 tunneling via SSH in OSX

-X isn't enough for the ssh args in OSX according to this post. So, "ssh -XY hoser@remotebox" worked just great when remoting in to a Suse server and firing executing fvwm.

Thursday, April 12, 2007

1024 vs 2048 RSA encryption/decryption fun in Ruby

Using Tobias's handy openssl wrapper I decided to run a couple timing tests to see how well Ruby's openssl implementation performed in encrypting/decrypting a set of 1000 identical messages, with 1024 bit and 2048 bit keys. Nothing scientific about this, just three runs of the script on my MacBook Pro (2.33 GHZ, 1G RAM).

Encrypted text: "This is a much longer message since than what I intend to encrypt"

Encryption results, avg time
1024: 0.000300691s
2048: 0.001010027s

Decryption results, avg time
1024: 0.005472064s
2048: 0.035524023s

Pick your poison.

Monday, April 02, 2007

One last hitch

According to this, friendly errors such as

/opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/mapping.rb:520:in `class_schema_variable': undefined method `class_variables' for nil:NilClass (NoMethodError)
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/mapping.rb:353:in `schema_ns_definition'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/mapping.rb:380:in `schema_definition_classdef'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/registry.rb:198:in `schema_definition_from_class'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/literalregistry.rb:73:in `any2soap'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/literalregistry.rb:37:in `obj2soap'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/literalregistry.rb:127:in `stubobj2soap'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/literalregistry.rb:114:in `each'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/literalregistry.rb:114:in `stubobj2soap'
... 15 levels...
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/rpc/proxy.rb:126:in `call'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/rpc/driver.rb:179:in `call'
from (eval):6:in `search'


prohibit the following default generated objects from correctly mapping when invoking the NetSuite searches:

  • OpportunitySearchBasic

  • EmployeeSearchBasic

  • PartnerSearchBasic

  • ItemSearchBasic

  • TimeBillSearchBasic


Each of which contain an entity named 'class', and while soap4r intelligently generates an attribute 'v_class', it also erroneously generates accessor methods that override Object.class. Big woops. Extremely easy fix just by renaming those methods.

Wednesday, March 28, 2007

NetSuite and Ruby, the final chapter

NetSuite requires a maintainable session for subsequent requests after the initial login by way of several cookies they return upon successful authentication. The standard Net::HTTP library requires mucking with headers by hand in order to perpetuate cookie information. We were easily able to override SOAP::NetHttpClient::Response and SOAP::HTTPStreamHandler in the soap4r gem in order to provide the functionality for maintaining the NetSuite session.

However
, NaHi's http-access2 library already provides cookie management (even persistence if needed). After installing visnu's gem it's simply a matter of turning off the SSLConfig#verify_mode and everything works flawlessly. The final test code:
require 'rubygems'
require_gem 'soap4r'
require 'defaultDriver' #generated by soap4r
require 'pp'

driver = NetSuitePortType.new
driver.wiredump_dev = STDOUT
driver.proxy.streamhandler.client.ssl_config.verify_mode = nil

passport = Passport.new
passport.email = '123123123@123123123.com'
passport.password = '123123123'
passport.account ="123123"
passport.role = RecordRef.new
passport.role.xmlattr_internalId = '123123123'

driver.login(LoginRequest.new(passport))

record = RecordRef.new()
record.xmlattr_internalId = '123123123'
record.xmlattr_type = RecordType::Customer

pp driver.get(GetRequest.new(record))

Everything said and done, this has been a good learning experience. But more than anything it has exposed one of the Ruby community's greatest weaknesses: documentation. I've known this since the beginning of my Ruby days (2004) and have grown accustomed to reading code. Nevertheless, that takes time, and usually more time than scanning decently-documented APIs. The other side of this story is that I understand the Japanese/English language barrier which may intimidate or otherwise prevent library maintainers from providing more documentation. In the end, Ruby is a community-driven language, which is not sponsored by massive corporate dollars (cough, Java, cough, .Net) and its beginnings have solid Japanese roots. Here's to hoping that documentation improves as more libraries become available and the language matures. In the mean time, thank goodness for Google, mailing lists, blogs and IRC. I believe that documentation practices exhibited by 37Signals and the Rails community are the guiding example, they're not perfect, but the situation continues to progress.