Viewing Category:
[
clear category selection]
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.
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.
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.
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.
CFML
|
Posted
6/13/10
@ 4:35 PM
by Joseph Lamoree
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.
CFML
|
Posted
6/5/10
@ 5:05 PM
by Joseph Lamoree
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.
Apache,
CFML
|
Posted
9/30/09
@ 7:17 PM
by Joseph Lamoree
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
CFML
|
Posted
9/1/09
@ 5:18 AM
by Joseph Lamoree
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?
CFML
|
Posted
8/22/09
@ 9:00 PM
by Joseph Lamoree
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.
CFML,
PayPal
|
Posted
6/18/09
@ 4:32 PM
by Joseph Lamoree
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.
CFML,
PayPal
|
Posted
6/16/09
@ 9:35 AM
by Joseph Lamoree
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.
This is a quick post about a project I've been working on, an application to manage scheduled jobs on the server. The system maintains a queue of running jobs, preventing multiple resource intensive jobs from overloading the server. I created a screencast demo and uploaded it to Vimeo. Unfortunately, there is a problem with one of the edit points that causes it to stop playing halfway through. I'll have to fix that when I get back from a short visit to San Francisco. The entire movie can be seen if you download the original file, linked to the bottom of the video details page.
One of the cool artifacts of the work on the job scheduler was a generic bean that uses the onMissingMethod() facility in ColdFusion. The component pretends to have all the getter and setting methods one would expect, given a known set of properties. This reduces the number is silly beans with a whole mess of getters and setters that don't do anything useful.
I've also been working quite a bit with jQuery and jQuery UI -- especially the dialog widget. It really takes the effort out of doing some complicated bits of user interface work.
CFML,
jQuery
|
Posted
12/29/08
@ 2:36 AM
by Joseph Lamoree
Today I worked on an application that I wrote several years ago to take a PDF with blank form fields, and merge in values from a database. I understand that ColdFusion 8 now supports several PDF functions now, but I wanted to rewrite the existing code from using native Java wrapped in a CFX, into plain CFML including Java classes from the iText library. I thought this would simplify hosting because the iText JAR comes with a stock installation of CF8. So, I went about writing a CFC to encapsulate the form field merge. Unfortunately, I later discovered that the version of iText included with CF8 is somewhat old. By using the current iText API documentation, I avoided using deprecated methods. I could have used the JavaLoader to add the newer version to the classpath. But instead, I used the deprecated syntax. At some point in the future, however, this code will work out of the box! :)
fields = [
["CertificateTitle", "Super Awesome Guy"],
["FirstName", "Joseph"],
["LastName", "Lamoree"],
["DateIssued", dateFormat(now(), "dddd, dd mmm yyyy")]
];
pdfWriter = createObject("java", "com.lowagie.text.pdf.PdfWriter");
perms = bitOr(0, pdfWriter.ALLOW_PRINTING);
encType = pdfWriter.STANDARD_ENCRYPTION_128;
pdfReader = createObject("java", "com.lowagie.text.pdf.PdfReader").init(pdfIn);
fos = createObject("java", "java.io.FileOutputStream").init(pdfOut);
pdfStamper = createObject("java", "com.lowagie.text.pdf.PdfStamper").init(pdfReader, fos);
pdfStamper.setEncryption(userPasswd.getBytes("UTF8"), ownerPasswd.getBytes("UTF8"), encType, perms);
pdfStamper.setFormFlattening(true);
pdfFields = pdfStamper.getAcroFields();
for (i = 1; i lte arrayLen(fields); i = i + 1) {
pdfFields.setField(fields[i][1], fields[i][2]);
}
pdfStamper.close();
I haven't yet tried this with Open BlueDragon, but I believe it will work if the correct version of iText is added to the classpath.
CFML
|
Posted
11/21/08
@ 2:29 AM
by Joseph Lamoree
I created a screencast showing the use of a jQuery plugin that I wrote to perform lazy loading of hierarchical data. When I was working on the application, I didn't see the information I was looking for, so I decided to put it out there myself. The quality is adequate, but it's only my first screencast published for public use, so I'll get better with practice. I'd appreciate comments on the content, presentation, and production.
I used iShowU to record a 1280x720 portion of my second monitor. That's why you don't see the Mac OS X menubar or the application switcher when I do Command + Tab. The resolution is native for HD DV 720p, which I then edit in Final Cut. This generates gianmormous working files, but doesn't require any rendering on import to the timeline. My Power Mac is pretty old, so doing the compression of the final screencast to MPEG-4 (H.264/AAC) takes a really long time. I've got my eye on a Mac Pro quad core machine to speed this process up. Also, I notice that my mouse pointer is not visible in Eclipse, so it's not visible as I'm moving it around while talking. The final file is almost 33 minutes long and weighs in at 105.4 Mb.
I uploaded the file to Vimeo, which supports HD streaming to a Flash player. You can watch the video there, or download the whole QuickTime movie as LazyTree Demo.
I will be packaging up the source code of the project shortly. I'll post another blog with a link to a zip file and the Subversion repository.
I was working on a project yesterday using cf_sebForm from the sebtags project. It's a very nice library for doing, among other things, form field validation at the client and server using the same set of rules. Steve Bryant did a comprehensive presentation at a recent ColdFusion Meetup. The Adobe Connect recording URL is at UGTV.
Using the cf_sebField, it's ridiculously simple to create an assortment of form fields. The tag will create a dropdown list (HTML SELECT/OPTION elements) using a database table, a query, or a list of value/label pairs. See the cf_sebField documentation for the usage of subtable, subquery, subvalues, and subdisplays. I wanted another way to populate the tag; specifically, with an array of structures. My change adds a subarray attribute and functions the same way that subtable does. For example, consider creating a list of options using ColdFusion 8's implicit array and structure creation syntax. Then passing it to cf_sebField.
<cfset variables.options = [
{value="1", display="One"},
{value="2", display="Two"},
{value="3", display="Three"}
] />
<cf_sebField type="select" subarray="#variables.options#" subvalues="value" subdisplays="display" />
In my implementation, I actually have the options definitions inside a CFC as a static property with a public getter. This allows any other code in the system to get the same array of structures, such as when resolving a value to a user-friendly display string.
"Why an array of structures?" you ask. "Wouldn't it be easier to have a simple structure with the value as the key, and the display string as the value?" Yes, it would. But then the order of the options wouldn't be right. Using an array keeps them in the proper order. :)
CFML
|
Posted
5/6/08
@ 2:09 AM
by Joseph Lamoree
This is an old tip, but I'm posting it here because I couldn't find the last project that I used it with, and it was driving me crazier trying to recreate it from scratch -- I had a stupid XSL bug that wouldn't go away.
Anyway, the use case is that you have an XML document object that you want to display with nice formatting so it's easy for a human to read (line breaks and indentations). Here's the code.
<!--- Create the (case-sensitive) XML document as appropriate --->
<cfset xml = xmlNew(true)/>
<cfset xml.xmlRoot = xmlElemNew(xml, "dangers")/>
<cfset xml.xmlRoot.xmlChildren[1] = xmlElemNew(xml, "marmots")/>
<cfset xml.xmlRoot.xmlChildren[1].xmlAttributes["hungry"] = "true"/>
<cfset xml.xmlRoot.xmlChildren[1].xmlAttributes["appetite-for"] = "radiator hoses, electrical wiring insulation, etc."/>
<cfset xml.xmlRoot.xmlChildren[2] = xmlElemNew(xml, "zombies")/>
<cfset xml.xmlRoot.xmlChildren[2].xmlAttributes["hungry"] = "true"/>
<cfset xml.xmlRoot.xmlChildren[2].xmlAttributes["appetite-for"] = "brains"/>
<!--- The assumption is that this will be inside a block of code in which cfoutputonly is true, therefor the cfoutput around the XSL below --->
<cfsavecontent variable="xsl">
<cfoutput>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xalan" version="1.0">
<xsl:output indent="yes" method="xml" xalan:indent-amount="4"/>
<xsl:template match="/">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
</cfoutput>
</cfsavecontent>
<cfset xsl = reReplace(xsl, "^\s*", "")/>
<cfset buffer = xmlTransform(xml, xsl)/>
<!--- Dump the buffer out, or return it from a function --->
CFML
|
Posted
10/26/06
@ 1:08 PM
by Joseph Lamoree
Charlie Arehart posted a comment to my blog post about setting the last modified date on a file using some CFML and Java. If a person just wants to set the last modified date of a file to the current system time, his solution is much simpler. I'm reposting his comment here because it's easier to read when formatted as a blog post.
<cfscript>
function touch(filepath) {
fileInstance = createObject("java", "java.io.File").init(toString(filepath));
currentTime = createObject("java", "java.util.Date").init();
fileInstance.setLastModified(currentTime.getTime());
}
</cfscript>
CFML,
Java
|
Posted
10/6/06
@ 1:18 PM
by Joseph Lamoree
I did a bit of testing this morning to answer a message on the CF-Talk list. After my recent work with Java locale and time zone stuff, I thought this would be a good experiment to mix in a bit of java.io.File.
<!--- A plain file upload form --->
<form method="POST" enctype="multipart/form-data">
<input type="file" name="upload"/><br/><br/>
<input type="submit" value="Upload"/>
</form>
<hr/>
<!--- Configuration --->
<cfset tempDirectory = createObject("java", "java.lang.System").getProperty("java.io.tmpdir")/>
<cfset directorySeparator = createObject("java", "java.lang.System").getProperty("file.separator")/>
<cfset modDateTime = "10/28/1971 12:14 PM"/>
<!--- Save incoming file --->
<cfif structKeyExists(form, "upload")>
<cffile action="upload" filefield="upload" destination="#tempDirectory#" nameconflict="MakeUnique" mode="644" result="result"/>
<cfset clientFileLastMod = dateFormat(result.timeLastModified, "mm/dd/yyyy") & " " & timeFormat(result.timeLastModified, "hh:mm TT")/>
<cfoutput><p>File #result.clientFile# was saved as #result.serverFile# with last modified date of #clientFileLastMod#.</p></cfoutput>
<!--- Set our own last modified time --->
<cfset dateFormatterClass = createObject("java", "java.text.DateFormat")/>
<cfset dateTimeFormatter = dateFormatterClass.getDateTimeInstance(dateFormatterClass.SHORT, dateFormatterClass.SHORT)/>
<cfset timeZone = createObject("java", "java.util.TimeZone").getTimeZone("PST")/>
<cfset dateTimeFormatter.setTimeZone(timeZone)/>
<cfset parsedDateTime = dateTimeFormatter.parse(modDateTime)/>
<cfset uploadedFile = createObject("java", "java.io.File").init(result.serverDirectory & directorySeparator & result.serverFile)/>
<cfset uploadedFile.setLastModified(parsedDateTime.getTime())/>
<cfoutput><p>Setting last modified date on uploaded file to #modDateTime#.</p></cfoutput>
<cfdirectory action="list" directory="#uploadedFile.getParent()#" name="listing" filter="#uploadedFile.getName()#"/>
<cfdump var="#listing#"/>
</cfif>
CFML,
Java
|
Posted
9/14/06
@ 1:22 PM
by Joseph Lamoree