Viewing Category: CFML  [clear category selection]

(displaying entries 1 - 20)

ContentBox and PostgreSQL - Spring 2014 Edition

About twice a year, I download ContentBox and hope that I'll be able to use it to replace my old MachBlog installation. I just completed the Spring 2014 evaluation, and sadly I still can't make the switch. ContentBox 1.6 doesn't yet work with PostgreSQL 9.3. You can get pretty close if you do some hacking on several of the CFML ORM components:

  • Find all the components that define an ORM property of boolean type that use an integer for the dbdefault value. These need to be deleted. The components themselves have property defaults, so it shouldn't be necessary to create a column default in the database.
  • Modify modules.contentbox.model.content.Category to change the index name from idx_slug to something else, perhaps idx_category_slug. The stock name conflicts with the index in modules.contentbox.model.content.BaseContent.
  • Modify coldbox.system.orm.hibernate.BaseORMService to evaluate all criteria aimed at boolean columns and swap the integer values for booleans (t or f vs. 1 or 0).

You'll also need to manually create a sequence called hibernate_sequence because the Hibernate ORM PostgreSQL Dialect can't/won't create that when it creates all the DDL statements. You can cut-n-paste from here:

CREATE SEQUENCE hibernate_sequence START WITH 1 INCREMENT BY 1;

Once the ContentBox schema is setup, the ContentBox installer will try to save some initial configuration values. I gave up at this point. There were just too many exceptions in the ColdBox ORM Services when interacting with Hibernate against PostgreSQL. If I were to continue evaluating ContentBox, I'd abandon PostgreSQL for MySQL. I see that work is being done on ContentBox 2.0 for PostgreSQL compatibility. Maybe I'll try again in Fall.

Oh, and I should mention that I tried all the permutations of Railo 4.1.2.005, 4.2.0.000, and 4.2.0.00x, with Postgres.app and EnterpriseDB PostgreSQL 9.3.4 for Mac OS X. The bleeding edge Railo 4.2 has a new issue with WireBox due to a collision in the map() function name. Unfortunate, but unrelated.

Setting up ContentBox

I download a copy of ContentBox 1.6.0 tonight to experiment with it as a personal blog replacement. While I was at it, I grabbed the Apache Tomcat 7.0.52 and Railo 4.2.000 Beta. I'm giving the super-easy Postgres.app a try for local development.

After deploying the Railo WAR file, I went into the Railo Server Administrator to give it a password and set some preferences. I went into Settings - Scope and set cascading to Strict, among other things. I don't often get a chance to configure a CFML engine without regard to legacy applications, so I went hog wild.

I wrote an Apache Ant build script to fetch the ContentBox archive and deploy it into the Tomcat webapps path. I'll put all my source in a Github repo to share later. With ContentBox unzipped, I hit it with a browser. The ColdBox app redirected to a module called contentbox-dsncreator, prompting for the datasource name. I gave it the DSN created in Railo, pointing to my developer version of the database, and clicked the Verify Datasource button. I received a JavaScript alert after the page finished its Ajax request to /modules/contentbox-dsncreator/handlers/verifyDSN.cfm?dsnName=blog:

Error verifying datasource: attribute [datasource] is required, when no default datasource is defined you can define a default datasource as attribute [defaultdatasource] of the tag cfapplication or as data member of the Application.cfc (this.defaultdatasource="mydatasource";)

It's caused because the verifyDSN handler does what we've done in the CFML world for years and years:

<cfparam name="dsnName" default="">

Because I enabled strict scope cascading in Railo, the tag never finds the query string parameter. Setting that Railo option back to "Standard (CFML Default)" makes that line of code behave as intended. Any old-timer reading this will have just exclaimed "Well, duh. Of course." Besides being helpful to anybody that Googles that error message in the future, I'm writing all this is to argue that being scope-explicit is a good thing. I'm as guilty as the next guy/gal about these sorts of unscoped variable references, but I'd like to encourage the CFML community to change their practices. Write arguments when you expect something from the arguments collection; scope with url when you know the parameter is part of the query string; use the query's name when fetching column values, et cetera. Okay, I'll get off my SoapBox and get back to ContentBox now.

Of course, I'll fork the repo, make a tweak, and send a pull request. I mean, duh.

Reducing Unnecessary Tomcat Sessions

We have an internal application (Tomcat 7.0.x, Railo 3.3.x) that is configured with very long session timeout — 10 hours, in fact. This enables a user to login first thing in the morning and not have to worry about losing any session data, even after long periods of inactivity. However, we also have monitoring systems that check the application health every few minutes. Similarly, we have scheduled tasks that run throughout the day. In both of these cases, the HTTP user agents make a single connection, receive a response, and do not save any state between requests, i.e. cookies. This presents a bit of a problem; our application server will allocate a new session for each of these stateless connections and leave it in memory for 10 hours. Consider that one health check occurs every 3 minutes. The minimum number of sessions created before they start expiring is 200 (10 × 60 ÷ 3). That's not an insignificant amount of memory wasted. It would be ideal if we could expire those abandoned sessions sooner, or better yet, recycle a single session for use with these stateless user agents.

Tomcat has a solution. The Crawler Session Manager Valve will effectively gather requests made by the same user agent from the same IP address into a single session. Enabling the valve is as simple as adding the following into the conf/server.xml file:

<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">     <Valve className="org.apache.catalina.valves.CrawlerSessionManagerValve" crawlerUserAgents=".*ServerNanny.*|.*CFSCHEDULE.*" sessionInactiveInterval="600"/> </Host>

To verify that the valve is working as expected, make a few requests of the server:

for I in `jot - 1 100`; do curl -s -j -o /dev/null -A 'ServerNanny' 'http://tomcat:8080/app/'; done

Then open the Tomcat Manager and view your application's sessions. Even though there were 100 stateless requests thrown at the server, they should have been forced into a single session. One of the attributes within the session will be org.apache.catalina.valves.CrawlerSessionManagerValve. For example, here's what I see when view the session details:

Tomcat Session Details

In contrast, opening the same URL with Firefox, configured to ignore cookies, will result in a new session being created for each request. You can see the CFML application attribute created, and the serialized display of the session scope, which might be useful if you want to snoop on the data in this shared session.

Some things to keep mind

  • If access to your application server made only through a broker proxy, you'll want to use the Remote IP Valve to pass accurate client IP addresses to the Crawler Session Manager Valve. I'll discuss this in another post.
  • If the client does not send a User-Agent HTTP header, as is the case with Server Nanny, you'll need to insert a custom header into the request so it can be identified by the regex pattern in the valve configuration. I'll describe how I satisfied this requirement in another post.
  • You will probably want to have your application set username information in the session so they stand out as authenticated sessions in the Tomcat Manager list of sessions. You could even detect the crawlers and insert a fake username to make the shared session obvious.

OpenCF Summit 2012

I'm presenting today at OpenCF Summit on automating CFML application build, test, and deploy.

I'm actively working on my slides for two upcoming presentations. I've created a Github repository for the slides and the sample application: Automating CFML App Build, test, and Deploy

Pining for Ternary Operations in CFML

The lack of a ternary operation often makes for extra verbosity in CFML. When I'm pining for this feature, it's often when doing something like this:

<cfif structKeyExists(names, "name")> <cfset doSomethingInteresting(names.name)> <cfelse> <cfset doSomethingInteresting("default")> </cfif>

If CFML had a built-in function like the following, that would go a long way to appeasing me: <cffunction name="structValue" returntype="any" access="public" output="false"> <cfargument name="struct" type="struct" required="true"/> <cfargument name="key" type="string" required="true"/> <cfargument name="default" type="any" required="false"/> <cfif structKeyExists(arguments.struct, arguments.key)> <cfreturn arguments.struct[arguments.key]/> <cfelseif structKeyExists(arguments, "default")> <cfreturn arguments.default/> <cfelse> <cfreturn/> </cfif> </cffunction>

There is already a structFind(struct, key). If it was expanded to accept a default value, that would be bitchin'. Returning to the example above creating a structValue(struct, key, default) function makes for simpler way to call doSomethingInteresting(value):

<cfset doSomethingInteresting(structValue(names, name, "default"))/>

That's my $0.02.

Removing a double-slash sequence from URLs

Here's a mini, nay micro, tip on Java regex goodness. I often build up URLs from bits and pieces of strings where there is not a strict convention for whether a leading/trailing slash should exist before/after a directory. The easiest way to deal with this situation is to use the directory separator character gratuitously, and strip out doubles afterwards. Consider the following:

<cfset host = "localhost"/> <cfset filepath = "/downloads/file.txt"> <cfset earl = "http://" & host & "/context/" & filepath/>

Most web servers don't actually care if there's an extra double-slash where there should be just one, as in http://localhost/context//downloads/file.txt. However, there's a simple way to fix up the URL using the String.replaceAll() method. It takes a regular expression that supports negative lookbehind, which prevents the double-slash following the colon from being molested:

<cfset earl = earl.replaceAll("(?<!:)//", "/")/>

Boom! Done.

CFLDAP and master/slave failover

Anybody who has used tags like CFLDAP and CFHTTP much knows that the timeout attribute is frustratingly ineffectual. If the target server is down or otherwise slow to respond, it can hold up the CFML engine response. Charlie Arehart has a great post called CF911: Lies, Damned Lies, and CF Request Timeouts...What You May Not Realize in which he provides all the details surrounding the issue. I was recently writing some code to authenticate against Active Directory using LDAP. The requirement was to use the master server in normal operation, but failover to a slave Active Directory server. If CFLDAP is used to attempt a connection to the master while it is offline, it will cause the CFML engine to hang out waiting for a response for far longer than the number of milliseconds in timeout attribute.

I decided that before making the LDAP connection, I would check to see if a TCP socket could be created to the server. Using a bit of Java, it's pretty simple to test connectivity:

<cffunction name="isTcpServiceAlive" returntype="boolean" access="public" output="false">     <cfargument name="host" type="string" required="true"/>     <cfargument name="port" type="numeric" required="true"/>     <cfargument name="timeout" type="numeric" required="true" default="2000"/>       <cfset var socket = createObject("java", "java.net.Socket").init()/>     <cfset var address = createObject("java", "java.net.InetSocketAddress").init(javaCast("string", arguments.host), javaCast("int", arguments.port))/>       <cftry>         <cfset socket.connect(address, arguments.timeout)/>         <cfset socket.close()/>         <cfreturn true/>         <cfcatch>             <cfreturn false/>         </cfcatch>     </cftry> </cffunction>

If the respone from isTcpServiceAlive() is false, there's not much reason to try CFLDAP using the master:389. The timeout is in milliseconds, and I think 2 seconds is a pretty reasonable amount of time to wait for a service on the same LAN.

QueryParam Scanner from ANT

A little bit ago, I added VarScoper to my ANT build script. That was something I had wanted to do for a long time, and it was silly easy. I also wanted to add QueryParam Scanner, but it was a bit trickier. Today I spent some time with it and whipped a pretty decent solution, if I do say so myself. I dropped the application into the root of a ColdFusion instance. Then I interacted with it with my browser and watched which parameters are posted when the scan request is made. I gathered up all the options and made an ANT build script that posts to the QueryParam Scanner application, and parsed the XML for the result.

I took my working script and removed everything but the stuff for QueryParam Scanner: qpscanner-build.xml.txt. At the top of the file are configuration options. At the bottom is a sample ANT task that does the work. I added an option for whether the build should report a failure if any alerts are returned. Simple, right?

I should mention that this build requires the ant-contrib library for the post task. Other than that, it's stock ANT from Apache. Also, the author of QueryParam Scanner, Peter Boughton, has the source in a Git repository on Github.

Creating the Transfer Definitions Path as Needed

While firing up a clean installation of a web application, I got an error (as expected) that the Transfer definitions path did not exist. To make fresh deployment of a web application easier, I modified my Transfer configuration, which is done inside ColdSpring. I created a component that wraps the actual Transfer configuration, and will create a missing path if needed. The same result could have been achieved using AOP and inserting a before advice pointcut, but that too requires writing out a piece of generated code -- the problem I'm trying to solve. Here's the wrapper:

<!--- /model/transfer/TransferEnvironment.cfc ---> <cfcomponent extends="transfer.com.config.Configuration" output="false">   <cffunction name="init" returntype="TransferEnvironment" access="public" output="false">     <cfreturn super.init(argumentCollection=arguments)/>   </cffunction>     <cffunction name="setDefinitionPath" returntype="void" access="public" output="false">     <cfargument name="definitionPath" type="string" required="true"/>       <cfif not directoryExists(expandPath(arguments.definitionPath))>       <cftry>         <cfdirectory action="create" directory="#expandPath(arguments.definitionPath)#" mode="775"/>         <cfcatch>           <cfthrow type="TransferConfigurationException" message="The Transfer definition path (#arguments.definitionPath#) does not exist, and could not be created."/>         </cfcatch>       </cftry>     </cfif>     <cfset super.setDefinitionPath(arguments.definitionPath)/>   </cffunction> </cfcomponent>

The ColdSpring XML using the wrapper is practically unchanged from what it was when using the actual Transfer configuration component:

<bean id="transferFactory" class="transfer.TransferFactory" lazy-init="false">   <constructor-arg name="configuration">     <bean class="model.transfer.TransferEnvironment">       <property name="configPath"><value>${transferConfigFile}</value></property>       <property name="datasourceName"><value>${transferDatasourceName}</value></property>       <property name="definitionPath"><value>${transferCodeGenPath}</value></property>     </bean>   </constructor-arg> </bean> <bean id="transfer" class="transfer.com.Transfer" factory-bean="transferFactory" factory-method="getTransfer"/>

Railo Multi-web on Tomcat

Both Sean Corfield and Jamie Krug have written about configuring Railo for multiple web sites/contexts long ago. However, having just done this setup myself, I thought I'd take notes and share them. I created a Google Document called (surprisingly enough) Railo Multi-web on Tomcat. I'm going to add to it as I have gather more information.

Painlessly Updating Open BlueDragon

I recently wrote a modest shell script to make updating an installation of Open BlueDragon less cumbersome. I reported an issue with a nightly build, and I wanted to be able to switch between build versions reliably and quickly. The script, available as openbluedragon-nightly-updater.sh, will download and archive the nightly build and then deploy to web application server configured. My development environment is Apache Tomcat on Mac OS X 10.5/10.6. I create a directory in $HOME called Servers into which I download tarball distributions of Tomcat. I then create a symbolic link to the current version as ~/Servers/Tomcat. I use the ROOT context for simplicity. I keep old nightly builds in my ~/Downloads directory. Therefore, the configuration in the update script looks like this:

NIGHTLY="OpenBlueDragon-Nightly-`date +%Y-%m-%d`.zip" SERVER="$HOME/Servers/Tomcat" DEPLOY="$SERVER/webapps" CONTEXT="ROOT" URL="http://www.openbluedragon.org/download/nightly/openbd.war" ARCHIVE="$HOME/Downloads/$NIGHTLY"

The script has two options: (-m) minimal or full update, and (-f) local file or remote download. With the minimal update, it just replaces the $DEPLOY/$CONTEXT/WEB-INF/lib/OpenBlueDragon.jar file. The full update replaces all of the Open BlueDragon JAR files, as well as the administration application and manual. It does not, however delete the existing Open BlueDragon configuration in $DEPLOY/$CONTEXT/WEB-INF/bluedragon. This is important to me because I don't want to lose the current settings for datasources, mail, debugging, and whatnot. I also want to keep all the symbolic links to my CFML applications in $DEPLOY/$CONTEXT in tact.

While working on a problem today, I wanted to verify that the issue wasn't introduced with last night's build. I just ran the following commands to revert to last week's build:

~/Servers/Tomcat/bin/catalina.sh stop ~/Workspace/admin/coldfusion/openbluedragon-nightly-updater.sh -m -f \     ~/Downloads/OpenBlueDragon-Nightly-2010-06-12.zip ~/Servers/Tomcat/bin/catalina.sh start

In less time than it takes to get another Diet Coke, the installation of Open BlueDragon was running an arbitrary version. It turned out that the issue I was troubleshooting had nothing to do with Open BlueDragon -- it was an error in my MXUnit test case. To get running again on the latest version, I ran the script without any arguments.

Preventing SCCS Data Leaks

Many people, right or wrong, deploy CFML applications to the web server by performing a checkout a source code control system, such as Subversion or Git. This has the effect of placing repository information in directories with the rest of the files; ${APPROOT}/**/.svn and ${APPROOT}/.git, for example. It's possible that this repository information (containing the code in plain-text and configuration files) will be exposed by the web server. That would be bad.

Whether the repository data is visible to an HTTP client depends on several factors: the OS, the web server and configuration, the directory and file permissions and OS- and filesystem-specific attributes. Probably the two most common environments are Windows with IIS and Linux with Apache. In the first case, IIS by default is configured to hide files and directories with the NTFS hidden attribute. Since both Subversion and Git create their repository directories with this flag enabled, the default scenario on Windows/IIS is safe. However, the same is not true for Linux/Apache (or Apache on Windows, for that matter).

Apache has always shipped, to the best of my knowledge, with a server-wide directive to prevent disclosing .htaccess and .htpasswd files:

<FilesMatch "^\.ht"> Order allow,deny Deny from all Satisfy All </FilesMatch>

It's not enough, I'm afraid, to remove the "ht" from the regex. To properly secure the SCCS artifacts, I like to use the trusty mod_rewrite module:

RewriteRule /(\.svn|\.git)/.* - [L,F]

And while I'm on the topic of using mod_rewrite to secure an application, here are some rules I use to prevent any similar shenanigans:

RewriteRule ^/app/(config|filters|listeners|plugins|properties|views)/.* - [L,F] RewriteRule ^/(MachII|MachIIDashboard|coldspring|transfer|cfpayment)/.* - [L,F] RewriteRule ^/(db|gen|model|taglib)/.* - [L,F]

Please feel free to comment. Oh wait, I'm lame and haven't enable comments on this blog. I suppose you could send them to @jlamoree instead.

Absolute, Relative, and Temporary Paths

I've often needed to programmatically determine a component's relative (possibly mapped) path, as well as the absolute path (with symbolic links resolved) to the CFC file. Getting the absolute path is a cinch using the path element returned by the getMetadata() function. However, the relative path must be gleaned from the name by replacing the dot-notation with directory separators. No, it's not rocket surgery.

<cffunction name="getPaths" returntype="struct" access="private" output="false">   <cfset var md = getMetaData(this)/>   <cfset var paths = structNew()/>   <cfset var name = listLast(md.name, ".")/>   <cfset paths.relative = "/" & listChangeDelims(reReplaceNoCase(md.name, name, ""), "/", ".")/>   <cfif not right(paths.relative, 1) eq "/">     <cfset paths.relative = paths.relative & "/"/>   </cfif>   <cfset paths.absolute = getDirectoryFromPath(md.path)/>   <cfset paths.temp = getTempDirectory()/>   <cfreturn paths/> </cffunction>

Dumping out the structure returned by getPaths() yields the following:

The temp directory of the CFML engine is also returned, should you need that too.

Visually Unique Character Sets

A moment ago I asked both of my followers on Twitter about character sets that eliminate visually similar symbols. Neither of them replied yet, but I thought I'd expand on the question. I was working on a token maker that generates random sets of characters for use as unique identifiers or serial numbers. In the following code block, see the UNIQUE character set -- it doesn't contain characters that could be recognized incorrectly when printed or in an e-mail (1,l,0,O,2,Z 5,S...).

<cfset variables.characters = structNew()/> <cfset variables.characters.ALPHA_LOWER = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z"/> <cfset variables.characters.ALPHA_UPPER = ucase(variables.characters.ALPHA_LOWER)/> <cfset variables.characters.ALPHA = variables.characters.ALPHA_LOWER & "," & variables.characters.ALPHA_UPPER/> <cfset variables.characters.NUMERIC = "0,1,2,3,4,5,6,7,8,9"/> <cfset variables.characters.ALPHANUMERIC = variables.characters.NUMERIC & "," & variables.characters.ALPHA/> <cfset variables.characters.ALPHANUMERIC_LOWER = variables.characters.NUMERIC & "," & variables.characters.ALPHA_LOWER/> <cfset variables.characters.ALPHANUMERIC_UPPER = variables.characters.NUMERIC & "," & variables.characters.ALPHA_UPPER/> <cfset variables.characters.UNIQUE = "3,4,5,6,7,8,9,a,A,b,B,c,C,d,D,e,E,F,G,h,H,j,J,k,K,L,m,M,n,N,p,P,Q,r,R,t,T,u,U,v,V,w,W,x,X,y,Y"/> <cfset variables.characters.UNIQUE_LOWER = _listDeleteEmptyItems(reReplace(variables.characters.UNIQUE, "[A-Z]", "", "all"))/> <cfset variables.characters.UNIQUE_UPPER = _listDeleteEmptyItems(reReplace(variables.characters.UNIQUE, "[a-z]", "", "all"))/>

The resulting variable contains a variety of character sets that can be used to create any length of random string.

So, if you do know what such a unique character set is called, do let me know.

Oh, one other thing I should mention. I created the function _listDeleteEmptyItems() with a leading underscore in case one of the CFML engines implements a function of the same name. With my luck, it would happen next week.

Sending Files: mod_xsendfile vs. CFCONTENT

There is a blog post and comment thread on Ben Nadel's site about File Downloads Without Using CFContent that Sami Hoda alerted me to. Specifically, he pointed to comments about mod_xsendfile, an Apache module that serves files by scanning the output for a special HTTP header. This is really awesome in a CFML/JEE environment because the application server is freed up from waiting for a file to finish transfering.

I did some experimentation with mod_xsendfile v0.11 on Windows XP (yes, you read that correctly, on Windows) using Apache 2.2. It works beautifully. Here's an example of the web server configuration:

LoadModule xsendfile_module modules/mod_xsendfile-0.11.so <VirtualHost *:80> ServerName downloads DocumentRoot "C:/workspace/Download" XSendFile on XSendFileIgnoreEtag on XSendFileIgnoreLastModified on XSendFilePath "C:/Documents and Settings/jlamoree/My Documents/Downloads" <Directory "C:/workspace/Download"> AllowOverride all Order allow,deny Allow from all </Directory> </VirtualHost>

The experimental CFML reads a directory of files and displays a list of links, one for a download using mod_xsendfile, and another to download a file using cfcontent. The meat of the code is pasted below, but you can download the entire experiment as mod_xsendfile-experiment.zip.

<cfheader name="Content-Disposition" value="attachment; filename=""#url.filename#""" /> <cfif url.method eq "mod_xsendfile"> <cfheader name="Content-Type" value="application/octet-stream" /> <cfheader name="X-Sendfile" value="#local.filename#" /> <cfelseif url.method eq "cfcontent"> <cfcontent file="#local.filename#" reset="yes" deletefile="no" type="application/octet-stream" /> </cfif> <cfabort />

I created a quick and dirty JMeter test to compare both methods of sending a 8 Mb file. The first request using mod_xsendfile took 327 ms. The second request, only 99 ms. Using cfcontent the request times were 205 ms and 183 ms. So, take that with a grain of salt. In fact, use a whole salt shaker.

Beans: Merge and Extract

While working on a Customer and Address object relationship that is many-to-many, I wanted a way to send a single bean to the form that included all the fields of the customer, shipping address, and billing address. I modified BeanUtils to support merging two beans, while adding a prefix to property names. The reverse operation, pulling properties with a specified prefix out of a single bean, I've called an extraction.

Why go to all this trouble? Certainly this solves a very specific problem, however it works very well to mask the underlying aggregation or composition. Since the client will return all the properties in a single batch, it makes sense. Obviously, it's possible to split the client-server interaction up to separate the fields. Consider a blog form that allows dynamic creation and association with categories or tags using background AJAX.

At any rate, here's an example of preparing a bean to hand to the view layer that populates an HTML form:

var bu = getBeanUtils(); var customer = getCustomerService().getCustomer(customerId); var shippingAddressBean = bu.create("name,streetAddress,city,region,postalCode,country"); var billingAddressBean = bu.create("name,streetAddress,city,region,postalCode,country"); bu.transfer(customer, customerBean); if (customer.hasShippingAddress()) { shippingAddress = customer.getShippingAddress(); bu.transfer(shippingAddress, shippingAddressBean); bu.merge(customerBean, shippingAddressBean, "shippingAddress"); } if (customer.hasBillingAddress()) { billingAddress = customer.getBillingAddress(); bu.transfer(billingAddress, billingAddressBean); bu.merge(customerBean, billingAddressBean, "billingAddress"); } return customerBean;

When processing the user input, BeanUtils pulls the addresses out:

var bu = getBeanUtils(); var customerBean = _event.getArg("formBean"); var shippingAddressBean = bu.create("name,streetAddress,city,region,postalCode,country"); var billingAddressBean = bu.create("name,streetAddress,city,region,postalCode,country"); bu.extract(customerBean, shippingAddressBean, "shippingAddress"); bu.extract(customerBean, billingAddressBean, "billingAddress");

Following the property extraction, the beans can all be passed to the appropriate validator. Assuming all is well, the properties can be pushed back to the object instances that are persisted:

var bu = getBeanUtils(); var cs = getCustomerService(); var customer = cs.getCustomer(customerId); var shippingAddress = "null"; var billingAddress = "null"; bu.compose(customer, customerBean, "name,email,phone"); shippingAddress = customer.getShippingAddress(); bu.compose(shippingAddress, shippingAddressBean); shippingAddress.save(); billingAddress = customer.getBillingAddress(); bu.compose(billingAddress, billingAddressBean); billingAddress.save(); customer.save();

Hopefully that makes sense. I pulled out a lot of superfluous code, and it's not clear where each chunk is executing. My objective was to show how the bean utility assists in the data round trip. The current version of the bean utility is posted so you can see the implementation of the transfer, compose, merge, and extract methods: BeanUtils.cfc

More Beans!

I use a lot of beans in my CFML applications. When I use the term beans, I'm referring to a particular naming convention on components (CFCs), much like the JavaBean concept. My beans have getters/setters that all return/receive string values. This makes it possible for the MVC controller to populate a bean the user input, without any exceptions thrown due to incorrect data type or format. Validation of the bean properties happens later, and the results of the validation are stored inside the bean. When the bean is then handed off to a MVC view, the validation messages can be displayed alongside the field label. Consider the following model:

In most situations, the system instantiates a beans.Bean and calls the appropriate getter and setter methods. However, this particular component doesn't actually have any getters or setters -- it has an onMissingMethod() handler that mimics their behavior. This generic bean can stand in place of any real bean. It creates its own validation.ValidationMessages instance and uses a datatype.Collection to keep track of its properties. Because it always knows what properties it contains, other components can easily move values in and out of the bean. A beans.BeanUtils component exists to simplify the common task of loading and unloading beans:

None of the information I've covered so far is particularly new or innovative. However, I recently encountered a special situation using the beans.Bean that prompted me to do some experimenting. While working on a search interface (think: Google advanced search options), I wanted to put all the search information into a bean to pass off to the search service. However, I also wanted a method (unrelated to getters and setters) that returns a properly URL-encoded string for use in the view. It seemed trivial to extend the bean and add the getSearchArgs() method. Doing so, unfortunately, breaks the onMissingMethod() mechanism for supporting arbitrary getters and setters. The solution is to implement the getters and setters using the extended component's managed structure.

<cfcomponent extends="beans.Bean"> <cffunction name="init" returntype="SearchBean"> <cfset super.init("query,...") /> <cfreturn this /> </cffunction> <cffunction name="getSearchArgs" returntype="string"> <!-- Return URL-encoded arguments --> ... </cffunction> <cffunction name="getQuery" returntype="string"> <cfreturn getProperty("query") /> </cffunction> <cffunction name="setQuery" returntype="void"> <cfargument name="query" type="string" required="true" /> <cfset setProperty("query", arguments.query) /> </cffunction> ... </cfcomponent>

The getProperty() and setProperty() methods come from the extended component (super class), and the init method (constructor) sets up the appropriate property names. The onMissingMethod() mechanism for supporting dynamic property getter/setters is not used, or needed. Simple, right?

Using cfpayment

cfpayment diagram

As I've been integrating cfpayment with my commerce application, I've found it handy to have a diagram showing the important bits of the system. This diagram (click the thumbnail for a legible version) shows the bean-like methods in brown, and the methods that would be used by an application using the software in red. The methods in plain black are interesting, but should not have the focus. I should note that the diagram is not a faithful UML Class Diagram of the software, obviously. Some private properties and methods are left out for (an attempt at) clarity. There is also no such abstract class as cfpayment.model.Account; the credit card, EFT, and token only inherit from it by convention. At any rate, I hope it's helpful.

Testing PayPal Gateway Express Checkout

While working on the PayPal Website Payments Pro gateway for the cfpayment project, I had to fiddle with the typical unit testing procedure. The PayPal Express Checkout process involves a user interacting with the PayPal website between the start and completion of a payment transaction. The documentation at PayPal has a pretty good overview of the flow, from the two supported entry points.

If you look at the source from Subversion, you'll see that the NVPGatewayTest.testBeginExpressCheckout() method first sends the SetExpressCheckout command to PayPal, then redirects the user to the PayPal site. Once the user authenticates at PayPal and confirms some profile information, they are returned to the unit test at NVPGatewayTest.testCompleteExpressCheckout(), bring along two URL parameters: token and payerId. The test then performs two requests of PayPal: GetExpressCheckoutDetails and DoExpressCheckoutPayment.

This seems to be working pretty well. I recorded a screencast of the process showing the test from start to finish: PayPal Gateway Express Checkout Testing.

Configuring a Production Open BlueDragon Server

I've just finished building up a couple production servers to host web applications. The servers are Xen guests on an AMD Quad-Core Opteron x86_64 host. The VPS template is a minimal installation of CentOS, to which I added packages as needed. The release of Sun Java 1.6u12 came out just as I was writing this, so these instructions will need to get updated slightly when JPackage has a new RPM (more on that later). Both Matt Woodward and Dave Shuck recently wrote about configuring CFML engines with Tomcat. The installation I'll describe is somewhat similar.

  • CentOS 5.2
  • Tomcat 5.5.23 (tomcat5-5.5.23-0jpp.7.el5_2.1)
  • Apache 2.2 (httpd-2.2.3-11.el5_2.centos.4)
  • Sun Java 1.6u11 (java-1.6.0-sun-1.6.0.11-1jpp)
  • Sun JavaMail 1.4.1
  • Open BlueDragon 1.0.1

The installation of packages using yum is a snap, however there was an issue with the architecture detection. There is a simple workaround, to hard-code i386 as the basearch:

sed -i -r 's/\$basearch/i386/g' /etc/yum.repos.d/CentOS-Base.repo

The procedure is to install jpackage-utils, then download and repackage the Sun Java SE Development Kit 6 (jdk 1.6) using the JPackage Project non-free nosrc RPM. I install some, but not all of the, resulting RPMs:

yum --nogpgcheck localinstall java-1.6.0-sun-1.6.0.11-1jpp.i586.rpm java-1.6.0-sun-devel-* java-1.6.0-sun-fonts-*

The CentOS Wiki has a thorough article on installing Java on CentOS. I've considered using OpenJDK, but I don't know what sort of compatibility issues that would raise.

The Tomcat server starts up just fine with GNU's version of the Java runtime (libgcj and java-1.4.2-gcj-compat). However, using the GNU version of JavaMail (classpathx-mail) instead of Sun JavaMail, the following chunk of CFML will fail with a javax.mail.NoSuchProviderException exception from within the Open BlueDragon web application:

<cfscript> server = "localhost"; port = 25; username = ""; password = ""; mailSession = createObject("java", "javax.mail.Session").getDefaultInstance(createObject("java", "java.util.Properties").init()); transport = mailSession.getTransport("smtp"); transport.connect(server, JavaCast("int", port), username, password); transport.close(); </cfscript>

Open BlueDragon does include include the correct Jar, but the JVM that Tomcat configures loads the system version first. Rather that muck about with the classpaths, I downloaded the current version of JavaMail, extracted mail.jar, and created alternatives link:

unzip -j -d /tmp javamail-1_4_1.zip javamail-1.4.1/mail.jar mv /tmp/mail.jar /usr/share/java/javamail-1.4.1.jar alternatives --install /usr/share/java/javamail.jar javamail /usr/share/java/javamail-1.4.1.jar 5000 alternatives --auto javamail file /var/lib/tomcat5/common/lib/\[javamail\].jar

Tomcat installs a set of symlinks to /usr/share/tomcat5. Configuration files are placed in /etc/tomcat5. For this installation, I use a stripped-down version of server.xml that provides web application hosting on a per-user basis.

<Server port="8005" shutdown="SHUTDOWN"> <GlobalNamingResources /> <Service name="Catalina"> <Connector port="8080" address="127.0.0.1" protocol="HTTP/1.1" /> <Connector port="8009" address="127.0.0.1" protocol="AJP/1.3" /> <Engine name="Catalina" defaultHost="localhost"> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" debug="0" /> <Host name="localhost-username" appBase="/home/username/webapps" unpackWARs="false" autoDeploy="false" debug="1"> <Context path="" docBase="openbd" allowLinking="true" caseSensitive="true" swallowOutput="true" /> </Host> </Engine> </Service> </Server>

The standard Tomcat configuration has a single Host within an Engine named Catalina. I've added a second Host that is specific to a system user username, which allows each user on the system to manage their own deployed web applications and choose their own root Context. Installing Open BlueDragon as the default web application simplifies the Apache HTTP configuration.

The username user has an Apache HTTP configuration file in /etc/httpd/conf.d/username.conf with mod_rewrite rules to proxy all requests for CFML files to the Tomcat HTTP Connector. I had intended to use the AJP Connector with mod_proxy_ajp, but there is a problem with the the proxy request not specifying the proper hostname. There might be a solution to that issue, but I haven't found it yet. The plain mod_proxy_http module works properly in the following configuration:

<VirtualHost *:80> DocumentRoot /home/username/websites/sitename ... RewriteCond %{SCRIPT_FILENAME} \.cfm$ RewriteRule ^/(.*)$ http://localhost-username:8080/$1 [P] </VirtualHost>

The rest of the Apache HTTP configuration handles web requests for flat files, served from ~/websites/sitename. The CFML files can be placed in ~/webapps/openbd, however an easier deployment is to place everything in ~/websites/sitename (like you would with a typical ColdFusion server). Symbolic links can be added for directories containing CFML. Consider the following:

cd ~/webapps/openbd ln -s ../../websites/sitename/MachII MachII

It would probably be a good idea to set the Open BlueDragon root mapping appropriately. There are a few issues with file ownership and permissions that I didn't address above. I've added username to the /etc/sudoers file, granting that user limited access.

  next >