Serving JSP pages with Apache httpd and Tomcat

If you want *.jsp files to be served directly from your favorite Apache (2.x) webserver (port 80) rather than from Tomcat's built in webserver at port:8080 follow the instructions below.

I assume you have Apache (2.x) as well as Tomcat installed. Make sure Tomcat works by itself by requesting the samples at http://localhost:8080/examples/jsp.

4 steps involved:

1. Download mod_jk.so from http://tomcat.apache.org/download-connectors.cgi/. There's different versions for different platforms. Download the appropriate one to your Apache/modules dir then make sure to rename it mod_jk.so.

2. Go to the Apache config directory (where httpd.conf is located) and create a file named workers.properties containing the next few lines:


worker.list=worker1
worker.worker1.type=ajp13
worker.worker1.host=localhost
worker.worker1.port=8009

3. The above defines a so called worker named worker1 on port 8009 (where Tomcat gets it's forwards). Apache httpd must forward all *.jsp requests to this worker. So let's edit httpd.conf to get that done (edit the file paths to match your configuration).


# Load mod_jk module
LoadModule jk_module modules/mod_jk.so
# Where to find workers.properties
JkWorkersFile C:/Apache/conf/workers.properties
# Where to put jk shared memory
JkShmFile C:/Apache/logs/mod_jk.shm
# Where to put jk logs
JkLogFile C:/Apache/logs/mod_jk.log
# Set the jk log level [debug/error/info]
JkLogLevel info
# Select the timestamp log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
# Send all requests to *.jsp to the worker named worker1 (ajp13)
JkMount /*.jsp worker1

4. Restart Apache and there you go. You could copy the examples directory from tomcat/webapps to your Apache webroot to see if everything is OK: you should be able to run the examples at http://localhost/examples/jsp (so without the port 8080 part).

All this and more can be found at http://tomcat.apache.org/connectors-doc/

Running Tomcat alongside Coldfusion (Apache webserver)

When there's a Coldfusion server installed the above needs some more tuning. That's because Coldfusion by default handles all *.jsp requests, so Tomcat won't even know about you requesting jsp pages. Coldfusion (JRun) may be able to handle jsp very well, however if you have a Standard License installed then upon requesting a jsp page CF very kindly informs you that you are trying to access a restricted feature. You must upgrade to Enterprise Edition to enjoy this feature.

That's a cool suggestion perhaps, but we'll stick to Tomcat anyway. All we have to do is tell Coldfusion not to handle jsp requests anymore (or rather: stop telling Coldfusion to handle jsp requests).

2 steps involved:

1. On installation Coldfusion configures Apache to have a whole range of file types to be handled by the CF application server.

Open Apache's httpd.conf then look for Loadmodule jrun_module. Check the lines below. Set JRunConfig Ignoresuffixmap true and remove the '.jsp' file type from Addhandler. Leave the rest as it is.


<IfModule mod_jrun20.c>
...
JRunConfig Ignoresuffixmap true
...
# removed from Addhandler: .jsp
AddHandler jrun-handler .cfm .cfml .cfc
</IfModule>

2. Restart Apache and get yourself a beer.

Auto-updating AJAX controls

When several Ajax controls are spread throughout an interface it can be hard to keep them all synchronized with each other and the data they represent. Say your GUI shows a list of addresses in a CFDIV. Elsewhere in the GUI there's a CFSELECT showing addresses. Now if you delete an address from the list asynchronously, so that the CFDIV is reloaded without reloading the entire page, then the zelectbox must reflect these changes as well.

How would you tell the selectbox it has to update it's contents?

[More]

Resultset Pagination



<!---

    usage:
    
    count:            total records (numeric)
    size:            number of records per page (numeric)
    resultpage:        current page on display (numeric)
    query:            query result (query)    
    useAjaxLink:    whethet to use CF's AjaxLink function for links
    
    <cf_pagination count resultpage query size>

--->


<cfif thistag.executionMode eq "Start">

    <!--- optional attributes --->
    <cfparam name="attributes.count" default="0" type="numeric"/>
    <cfparam name="attributes.resultpage" default="0" type="numeric"/>
    <cfparam name="attributes.query" default="" type="query"/>
    <cfparam name="attributes.size" default="10" type="numeric"/>
    <cfparam name="attributes.useAjaxLink" default="false" type="boolean"/>
    
    <cfif not isDefined("attributes.query") or not isQuery(attributes.query)>
        <cfabort showerror="please supply a recordset" />
    </cfif>

    <!--- treat form or url vars equally --->    
    <cfscript>
        if (NOT IsDefined("formOrUrl"))
        formOrUrl=structNew();
        StructAppend(formOrUrl, url, "no");
        StructAppend(formOrUrl, form, "no");
    
</cfscript>
    <!--- <cfdump var="#formOrUrl#"> --->
    
    <cfset qs = ''>
    <cfloop collection="#formOrUrl#" item="field">
        <cfset qs &= field & '=' & formOrUrl[field] & '&'>
    </cfloop>
    
    <!--- remove this extra argument from form post --->
    <cfset qs = reReplaceNoCase(qs,'&?fieldnames=[^&]*','')>
    <!--- repeat entire query string except for resultpage --->
    <cfset qs = reReplaceNoCase(qs,'&?resultpage=[^&]*','')>
    <!--- when using CF's AjaxLink function all _cf_*** type arguments must be left out too --->
    <cfset qs = reReplaceNoCase(qs,'&?_cf_[^=]+=[^&]*','','all')>
    <!--- remove leading and trailing &'s --->
    <cfset qs = reReplace(qs,'^&','')>
    <cfset qs = reReplace(qs,'&$','')>
    
    <cfset curl = getFileFromPath(CGI.script_name) & "?" & qs >

    <cfset totpages = ceiling(attributes.count/attributes.size)>

    <!--- Build browsing string --->
    <cfset n = MAX(1,MIN(attributes.resultpage - 5,totpages-9))>
    <cfset m = MIN(totpages,n + 9)>

    <cfoutput>
        
        <!--- #totpages#-#n#-#m#<br> --->    
        
        <cfif attributes.resultpage GT 1>
            <a href="#getLink(curl&'&resultpage=1')#"><<</a>
            <span class="spacer">  </span>
            <a href="#getLink(curl&'&resultpage='&attributes.resultpage-1)#"><</a>
        <cfelse>
            <span class="disabled"><<</span>
            <span class="spacer">  </span>
            <span class="disabled"><</span>
        </cfif>                
        <cfloop from="#n#" to="#m#" index="i">
            <cfif i EQ attributes.resultpage>
                <span class="active">#i#</span>
            <cfelse>    
                <a href="#getLink(curl&'&resultpage='&i)#">#i#</a>                
            </cfif>
        </cfloop>
        <cfif attributes.resultpage LT totpages>
            <a href="#getLink(curl&'&resultpage='&attributes.resultpage+1)#">></a>
            <span class="spacer">  </span>            
            <a href="#getLink(curl&'&resultpage='&totpages)#">>></a>
        <cfelse>
            <span class="disabled">></span>
            <span class="spacer">  </span>                
            <span class="disabled">>></span>
        </cfif>
                
    </cfoutput>            
    
    <cfscript>
        
        function getLink(cUrl) {
        
            if (attributes.useAjaxLink)    return AjaxLink(cUrl);
            else return cUrl;
        }
    
    
</cfscript>
    
</cfif>

Ajax Combobox Customtag

Ever wanted a real combobox in your HTML application? A combobox is a select box in which you can type text to filter out the options. Since such an element does not exist in HTML, here's one emulated.

Data is loaded asynchronously only after so many characters are typed, so this makes for excellent use with result sets that would otherwise create too large option lists. You make sure the bind url doesn't return too many options, otherwise increase the minimum number of required characters.

When it's used to display a field of an existing record you can pre-populate the list with one option/value pair.

Here we use CF's built in data binding, so you must use CFForm, but on the other hand there's no line of javascript you have to write yourself.


<!---

    usage:
    
    fieldname:        select's NAME attribute
    minlength:        minimum number of characters to be typed before filtering is possible
    buttonText:        text at button ('go')
    bindUrl:        url to get data from (it must return JSON, e.g. if you have a query: serializeJSON(query))    
    displayCol:        column to display as option
    valueCol:        column to set as options value
    defaultOption:
    
    <cfmodule template="tags/filteredSelect.cfm"
     fieldname="seladres" minlength="3"
     bindUrl="index.cfm?event=addresses.toJson&name="                
     displayCol="NAME" valueCol="ID"            
     buttonText="zoek"    
/>    
    ONLY TO BE USED INSIDE CFFORM!

--->


<cfif thistag.executionMode eq "Start">

    <!--- attributes --->
    <cfparam name="attributes.fieldname" default="" type="string"/>
    <cfparam name="attributes.minlength" default="3" type="numeric"/>
    <cfparam name="attributes.buttonText" default="go" type="string"/>
    <cfparam name="attributes.bindUrl" default="" type="string"/>
    <cfparam name="attributes.displayCol" default="" type="string"/>
    <cfparam name="attributes.valueCol" default="" type="string"/>
    <cfparam name="attributes.defaultOptionText" default="" type="string"/>
    <cfparam name="attributes.defaultOptionValue" default="" type="string"/>
    
    <cfset rand = right(createUUID(),16)>
        
    <cfoutput>

        <input type="text" id="filter#rand#" size="10"
         onkeyup="
            var b=document.getElementById('btnfilter#rand#');
            if(this.value.length>2){b.value=this.value;b.removeAttribute('disabled');}
            else {b.value='';b.setAttribute('disabled','disabled');}
        "
>

        <button type="button" id="btnfilter#rand#" value="" disabled='disabled'>#attributes.buttonText#</button>
    </cfoutput>    
        
    <cfselect name="#attributes.fieldname#" style="width:200px"
     bind="url:#attributes.bindUrl#{btnfilter#rand#@click}"
     display="#attributes.displayCol#"
     value="#attributes.valueCol#"
     bindOnLoad="false"
    >
                
            <cfif attributes.defaultOptionText neq ''>
                <cfoutput><option value="#attributes.defaultOptionValue#">#attributes.defaultOptionText#</option></cfoutput>
            </cfif>    
    </cfselect>


</cfif>    

Welcome

Welcome to this blog. Here I will share some code and thoughts on ColdFusion, Model-Glue, Fusebox, Transfer ORM, MySQL, XSLT and maybe even some PHP or Java or whatever other topic I deem apt.

Updates will be posted very irregularly.

Code examples are free to use and modify, but without warranty. Use at your own risk.

BlogCFC was created by Raymond Camden. This blog is running version 5.9.004. Contact Blog Owner