Aug 17
Scaffolding a Generic Admin - Part 5 - Dispatcher, Transfer and ColdSpring Setup and Config
Transfer , Dispatcher , ColdSpring Add comments
I introduced a lot of pieces to the puzzle during parts 1 through 4 of this series and with this post, I hope to put them together. We'll look at a file and folder setup, ColdSpring and Transfer XML configuration files and the other files that form application for the generic admin...Dispatcher package
From the Dispatcher zip file extract the folder com to your web root, or some other location. If you choose the latter, you will need to create ColdFusion mapping or use an Apache Alias or IIS virtual directory.
Modifying the Dispatcher sample app
The Hello World sample app included in the zip file is designed to be setup as an Apache virtual host. I'll explain the steps to get you started with a local development url in the form http://triviagame.
Follow these steps:
1. Extract the helloworld folder from the Dispatcher zip to wherever you like to place your web projects.
2. Rename the folder to "triviagame". Your folder structure should appear as below.
3. Open the Apache httpd.conf file and copy and paste the following. Renaming the "path/to/" portion with the actual path to triviagame on your system.
3. Open the Apache httpd.conf file and copy and paste the following. Renaming the "path/to/" portion with the actual path to triviagame on your system.
<VirtualHost 127.0.0.1:80>
DocumentRoot "path/to/triviagame/root"
Alias /config "path/to/triviagame//config"
Alias /listener "path/to/triviagame/listener"
Alias /model "path/to/triviagame/model"
Alias /view "path/to/triviagame/view"
Alias /com "path/to/com" #use if you prefer to map to Dispatcher source
ServerName triviagame
DirectoryIndex index.cfm
ErrorLog logs/triviagame-error_log
CustomLog logs/triviagame-access_log common
</VirtualHost>
4. Open your hosts file and add the entry
127.0.0.1 triviagame
5. Restart Apache. On a Mac the command line is:
sudo apachectl graceful
Okay, if all is well, you have a basic Dispatcher application available at http://triviagame/. Try it out.
If you are up and running, let's open DispatcherConfig next.
<cfcomponent displayname="DispatcherConfig">
<cffunction name="init" access="public" output="false" returntype="config.DispatcherConfig">
<cfset variables.config = StructNew() />
<!--- keeping it simple. set application config properties here --->
<cfscript>
variables.config.defaultEvent = "default.view";
variables.config.defaultTitle = "Hello World Sample Application - Dispatcher (beta) ";
</cfscript>
<cfreturn this />
</cffunction>
<cffunction name="getConfig" access="public" output="false" returntype="any">
<cfreturn variables.config />
</cffunction>
</cfcomponent>
This is about as basic as it gets. Configuration settings in the form of a struct, that are accessed via the getConfig() method. Edit the title to your favourite catch phrase. Now let's open index.cfm.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us">
<meta name="keywords" content="">
<meta name="description" content="">
<title><cfoutput>#Request.event.getConfig("defaultTitle")#</cfoutput></title>
<link href="/css/sample.css" rel="stylesheet" type="text/css" />
</head>
<body>
<cfif Request.event.hasErrors()>
<cfinclude template="#Request.event.getView("error")#">
</cfif>
<div id="content">
<cfinclude template="#Request.event.getView("content")#">
</div>
</body>
</html>
The <title/> tag shows one way to access data from a RequestEvent object. In this case, the value of defaultTitle is returned by the method getConfig("defaultTitle"). If you wanted to access the entire config struct you could invoke getCollection("config") instead.
If you refresh the page after editing the defaultTitle in DispatcherConfig, the edit will not be visible, because the application will need to be re-initialized. To do this, append ?init=true to your browser location ala http://triviagame/?init=true.
The other configuration setting is defaultEvent which is set to "default.view". As mentioned in part 4 of the series. Every page request expects a url variables in the form cmd={object}.{action}. When no cmd is found, Dispatcher will fall back to the value of the defaultView config setting. Very unoriginal, I admit. A thousand pardons to anyone who might take offense. ;)
In the index.cfm code above there are two calls to the method getView(). It is the ViewManager listener's responsibility to set views as a key/value pair via the addView() method the RequestEvent object. So, where index.cfm invokes getView("content"), ViewManager adds the view as shown below.
<cfset arguments.event.addView("content","/view/HelloWorld.cfm") />
In order to add Transfer to the the mix, we will need to add the transfer.xml and datasource.xml files required by Transfer and edit the ColdSpring xml bean definition file. Here is the current beans.xml contents.
<beans>
<bean id="DispatcherConfig" class="config.DispatcherConfig" singleton="true" />
<bean id="Dispatcher" class="com.fancybread.event.Dispatcher" singleton="true">
<constructor-arg name="config">
<ref bean="DispatcherConfig" />
</constructor-arg>
</bean>
<bean id="ViewManager" class="listener.ViewManager" singleton="true" />
</beans>
To this we need to add.
<bean id="transferFactory" class="transfer.TransferFactory">
<constructor-arg name="datasourcePath"><value>${datasourcePath}</value></constructor-arg>
<constructor-arg name="configPath"><value>${configPath}</value></constructor-arg>
<constructor-arg name="definitionPath"><value>${definitionPath}</value></constructor-arg>
</bean>
<bean id="datasource" factory-bean="transferFactory" factory-method="getDatasource" />
<bean id="transfer" factory-bean="transferFactory" factory-method="getTransfer" />
<bean id="TransferConfig" class="config.TransferConfig">
<constructor-arg name="configPath"><value>${configPath}</value></constructor-arg>
</bean>
The first three definitions are taken from the transfer documentation as the recommended way to setup transfer using ColdSpring. The fourth definition is for the TransferConfig component that I created. It is not part of transfer, but is required in the context of creating the "generic admin". I'll explain the use later in the series.
I choose to store ColdSpring in application scope as application.beanFactory. The properties that are used in the code snippet above (${datasourcePath}, ${configPath} and ${definitionPath}) will have to be set prior to creating application.beanFactory. This is done in the main() method of Application.cfc (show below).
<cffunction name="main" access="private" output="false" returntype="void">
<!--- create coldspring beanFactory--->
<cfset application.beanFactory = createObject("component","coldspring.beans.DefaultXmlBeanFactory").init() />
<!--- load bean definitions --->
<cfset application.beanFactory.loadBeansFromXmlFile("#expandPath('..')#/config/beans.xml",true)/>
<!--- put dispatcher in application scope (example only, not required)--->
<cfset application.dispatcher = application.beanFactory.getBean("Dispatcher") />
<!--- get reference to ViewManager from beanFactory --->
<cfset ViewManager = application.beanFactory.getBean("ViewManager") />
<!--- add ViewManager as dispatcher listener --->
<cfset application.dispatcher.addListener(ViewManager) />
</cffunction>
To this we will add:
<cfset var params = structNew() />
<cfscript>
params.dsn = "triviagame";
params.datasourcePath = "../config/transfer/datasource.xml";
params.configPath = "../config/transfer/transfer.xml";
params.definitionPath = "/config/transfer/definitions";
</cfscript>
and change
<cfset application.beanFactory = createObject("component","coldspring.beans.DefaultXmlBeanFactory").init() />
to
<cfset application.beanFactory = createObject("component","coldspring.beans.DefaultXmlBeanFactory").init(structNew(),params) />
Next, we will have to create the folders and files that are specified in the params struct.
Below is the contents of datasource.xml
<?xml version="1.0" encoding="UTF-8"?>
<datasource xsi:noNamespaceSchemaLocation="../xsd/datasource.xsd" xmlns:xsi="http://www.w3.org.org/2001/XMLSchema-instance">
<name>triviagame</name>
<username></username>
<password></password>
</datasource>
And here is the contents of transfer.xml
<?xml version="1.0" encoding="UTF-8"?>
<transfer xsi:noNamespaceSchemaLocation="../xsd/transfer.xsd" xmlns:xsi="http://www.w3.org.org/2001/XMLSchema-instance">
<objectCache>
<defaultcache>
<scope type="instance"/>
</defaultcache>
</objectCache>
<objectDefinitions>
</objectDefinitions>
</transfer>
The code above has no object definitions. When we return to generating code with Illudium, we'll copy and paste the generated definitions in transfer.xml
I've covered a lot a ground with this post. To make things easier for folks who have limited time, I've enclosed the modified application folder as triviagame.zip
Next stop, some code generation and a look at the List, Form and Bean views that are central to the generic admin.
Feb 6, 2008 at 7:05 PM I am working with your example and Coldspring issues an error saying that config.TransferConfig was not found. Could you please explain more on this?
Feb 7, 2008 at 6:52 AM An earlier version of the code relied on a supplementary TransferConfig object. This is no longer required. Please remove the definition from the beans.xml file.
My apologies for the mix-up. :(