LiveCycle Data Services for ColdFusion Developers
-
Upload
steven-erat -
Category
Technology
-
view
890 -
download
4
description
Transcript of LiveCycle Data Services for ColdFusion Developers
LCDS Data Management for ColdFusion Developers
Steven Erat
Quality Assurance EngineerPongo Resume, LLC
Who am I?What I do professionally:
• 10 years experience with ColdFusion• 6 years experience with Flex• 4 years experience with LiveCycle Data Services
• Software Quality Assurance for Flex/CF apps - Pongo• Flex SDK Quality Assurance Engineer - Adobe• ColdFusion Quality Assurance Engineer - Adobe (Assigned testing for LCDS)• ColdFusion Gold Support Engineer - Allaire > Macromedia > Adobe
What I do in my spare time:
• Red Hat Linux Certified Engineer• talkingtree.com - Presently its my “occasional” blog for web related topics• stevenerat.com - Professional Photographer... My “other” life
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Demo: Contact Manager
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
History of LCDSFlex 1.0 (includes the SDK and Server) – Released March 2004Flex 1.5 – Released Nov 2004FDS (Flex Data Services) was decoupled from the Flex SDK in Flex 2.0 – Released 2006FDS was renamed LiveCycle Data Services – released as LC DS 2.5 in summer 2007LCDS 2.5.1 in fall 2007An open source version of remoting and messaging was released as Blaze DS in early 2008LCDS 2.6 was released in mid 2008LCDS 2.6.1 was released in Nov 2008LCDS 3.0 released Nov 2009
source: http://anilchannappa.org/2008/11/26/history-flex-lc-ds-fds-and-blazeds/
ColdFusion MX 7.02 first integrated with Flex Data Services (mid 2006)ColdFusion 8.0 tightly integrated LCDS Express Edition (install option) (mid 2007)ColdFusion 9.0 de-integrated LCDS (Sept 2009) - Download “LCDS Components” and LCDS 2.6.1 for manual installation
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
LCDS FeaturesMessaging
• mx:Producer, mx:Consumer• “pub / sub”
RPC Services • mx:HTTPService, mx:WebService, mx:RemoteObject• Proxy, white list, client authentication
Data Management• mx:DataService• Data Synchronization• Conflict Resolution• Pagination • Offline data persistence support (AIR)• Channels: AMF; RTMP, NIO
Enterprise Support from Adobe
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
BlazeDS FeaturesMessaging
•mx:Producer, mx:Consumer•“pub / sub”
RPC Services •mx:HTTPService, mx:WebService, mx:RemoteObject•Proxy, white list, client authentication
Data Management•mx:DataService•Data Synchronization•Conflict Resolution•Pagination •Offline data persistence support (AIR)•Channels: AMF; RTMP, NIO
Enterprise Support from AdobeOpen SourceFreeAMF over HTTP channel (no RTMP)
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
LCDS EditionsBlazeDS
•Open source and free•Provides Messaging and RPC services•AMF and HTTP channels, but not RTMP •Hundreds of clients per CPU• Some support options for additional purchase
LCDS Community Edition•Exactly the same as BlazeDS but with Adobe support
LCDS Single CPU Edition (Express Edition) - (No longer available in LCDS 3)•Free•Messaging, RPC, and Data Management•AMF, HTTP, and RTMP channels•Thousands per CPU with RTMP / NIO channels•Shipped in CF8; Free to manually integrate/use with CF9
LiveCycle DataServices ES2• ~$60k/2CPU (min 2cpu)
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
LCDS Architecture (Integrated w/ CF)
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Advantages of Using LCDS with ColdFusion
ColdFusion is awesome!
Integrates into same JVM (ColdFusion 8, 9) •Faster than connecting CF to LCDS over RMI
ColdFusion DataServices Adapter•Translates ColdFusion objects to ActionScript objects (& vice versa)
ColdFusion Data Management Event Gateway (ColdFusion 8, 9)•Modify data in ColdFusion, notifies LCDS of changes, LCDS notifies connected clients
ColdFusion CFC Wizard in Flex Builder
•Extension for Flex Builder 3•Generates the required CFCs: Assembler, DAO, VO•Generates the ActionScript VO
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
CF8: Integrated LCDS Installer
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
CF9: Manual LCDS IntegrationDue to ColdFusion 9’s “Enhanced Flash Remoting”, LCDS not an install optionDownload “LiveCycle Data Services 2.6.1 Components” & LCDS DataServices ES2Follow instructions in the README ********************************************************************INSTRUCTIONS FOR MANUAL INTEGRATION OF LCDS 2.6.1 WITH COLDFUSION 9********************************************************************To be able to use LiveCycle Data Services ES 2.6.1 with ColdFusion 9, you need to manually integrate it with ColdFusion installation.Note: The following instructions use the term lcds_install_root to refer to the installated home directory for LiveCycle Data Services ES 2.6.1.1) Shutdown your ColdFusion 9 server if its running.2) Copy the ColdFusion9/lib/flex-messaging*.jar files to a backup location. If you are running a multiserver instance or a J2EE installation, these files are in the WEB-INF/cfusion/lib directory.3) Copy the lcds_install_root/resources/lib/flex-messaging*.jar files and overwrite them into the ColdFusion /lib directory.4) Copy "LCDS2.6.1_for_CF9/WEB-INF/flex/jars/cfdataservicesadapter.jar" to ColdFusion WEB-INF/flex/jars.5) Also Copy the following SWC libraries from lcds_install_root/resources/frameworks to the CF9 directory: ï /libs/fds.swc file to the /WEB-INF/flex/libs directory ï /libs/player/playerfds.swc file to the /WEB-INF/flex/libs/player/ directory ï /locale/en_US/fds_rb.swc file over the /WEB-INF/flex/locale/en_US/ directoryOld Style(CF8 style) Flash Remoting steps=========================================6) Copy your CF8 WEB-INF/flex/ *.xml config files to ColdFusion 9 WEB-INF/flex/ folder 7) Start ColdFusion server.Enhanced Flash Remoting of CF9 steps====================================6) copy *.xml files from LCDS2.6.1_for_CF9/WEB-INF/flex directory to ColdFusion 9 WEB-INF/flex/. 7) If your LiveCycle Data Services applications use RTMP channel, open the ColdFusion WEB-INF/flex/services-config.xml file and uncomment "cf-rtmp" channel. By default it is commented. 8) If your old Channels( in service-config.xml) has the <instantiate-types> present under <serialization> xml tag, then either remove <instantiate-types>, or set it to true. Also set the <enable-small-messages> to False.9) Add your data management destinations from the old file to the new data-management-config.xml file. In ColdFusion 9 release, there are few changes in the destination and channel structure due to changes in the serialization process. The following properties have been moved from destination to channel level. You need to move these properties under <channel-definition > --> <properites> --> <coldfusion> from your For details, see "Changes in the XML configuration files for New Flash Remoting" in ColdFusion 9 Documentation. 10) Deploy your LCDS/Flex applications after recompiling them using new *-config.xml files, which has above mentioned changes11) Start ColdFusion server.
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
CF Admin: Flex Integration
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Configuration FilesWEB-INF/flex * services-config.xml * data-management-config.xml
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Configuration Files: Channelsservices-config.xml > channels (abbreviated)
<channels> <!-- CF Based Endpoints --> <channel-definition id="my-cfamf" class="mx.messaging.channels.AMFChannel"> <endpoint uri="http://{server.name}:{server.port}{context.root}/flex2gateway/" class="flex.messaging.endpoints.AMFEndpoint"/> ... </channel-definition>
<channel-definition id="cf-polling-amf" class="mx.messaging.channels.AMFChannel"> <endpoint uri="http://{server.name}:{server.port}{context.root}/flex2gateway/cfamfpolling" class="flex.messaging.endpoints.AMFEndpoint"/> <properties> <polling-enabled>true</polling-enabled> <polling-interval-seconds>8</polling-interval-seconds> ... </properties> </channel-definition> <!-- ColdFusion specific RTMP channel --> <!-- <channel-definition id="cf-rtmp" class="mx.messaging.channels.RTMPChannel"> <endpoint uri="rtmp://{server.name}:2048" class="flex.messaging.endpoints.RTMPEndpoint"/> <properties> <idle-timeout-minutes>20</idle-timeout-minutes> ... </properties> </channel-definition> -->
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Configuration Files: Debuggingservices-config.xml > debugging (use for development only)
<logging> <target class="flex.messaging.log.ConsoleTarget" level="Debug"> <properties> <prefix>[LCDS-CF9] </prefix> <includeDate>true</includeDate> <includeTime>true</includeTime> <includeLevel>true</includeLevel> <includeCategory>true</includeCategory> </properties>
<filters> <pattern>Endpoint.*</pattern> <pattern>Service.*</pattern> <pattern>Configuration</pattern> <pattern>Message.*</pattern> <!-- Enable LCDS debug info for ColdFusion --> <pattern>DataService.coldfusion</pattern> </filters> </target> </logging>
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Configuration Files: Debug Outputcfserver.log or {servername}-event.log11/12 12:23:14 user MessageBrokerServlet: init[LCDS-CF9]11/12/2009 12:23:15.204 [INFO] [Configuration] Adobe LiveCycle Data Services: 2.6.0.213876[LCDS-CF9]11/12/2009 12:23:15.296 [INFO] [Configuration] Endpoint my-cfamf-secure created with security: Noneat URL: https://{server.name}:{server.port}{context.root}/flex2gateway/cfamfsecure[LCDS-CF9]11/12/2009 12:23:15.297 [INFO] [Configuration] Endpoint java-amf created with security: Noneat URL: http://{server.name}:{server.port}{context.root}/flex2gateway/amf[LCDS-CF9]11/12/2009 12:23:15.298 [INFO] [Configuration] Endpoint java-polling-amf created with security: Noneat URL: http://{server.name}:{server.port}{context.root}/flex2gateway/amfpolling[LCDS-CF9]11/12/2009 12:23:15.299 [INFO] [Configuration] Endpoint java-secure-http created with security: Noneat URL: https://{server.name}:{server.port}{context.root}/flex2gateway/httpsecure[LCDS-CF9]11/12/2009 12:23:15.374 [INFO] [Configuration] Endpoint cf-rtmp created with security: Noneat URL: rtmp://{server.name}:2048[LCDS-CF9]11/12/2009 12:23:15.375 [INFO] [Configuration] Endpoint java-secure-amf created with security: Noneat URL: https://{server.name}:{server.port}{context.root}/flex2gateway/amfsecure[LCDS-CF9]11/12/2009 12:23:15.375 [INFO] [Configuration] Endpoint java-http created with security: Noneat URL: http://{server.name}:{server.port}{context.root}/flex2gateway/http[LCDS-CF9]11/12/2009 12:23:15.376 [INFO] [Configuration] Endpoint my-cfamf created with security: Noneat URL: http://{server.name}:{server.port}{context.root}/flex2gateway/[LCDS-CF9]11/12/2009 12:23:15.376 [INFO] [Configuration] Endpoint cf-polling-amf created with security: Noneat URL: http://{server.name}:{server.port}{context.root}/flex2gateway/cfamfpolling[LCDS-CF9]11/12/2009 12:23:15.468 [INFO] [Message.coldfusion] [CFEventGatewayAdapter] Configuring ColdFusion Event Gateway adapter for destination ColdFusionGateway[LCDS-CF9]11/12/2009 12:23:15.521 [INFO] [DataService.coldfusion] [CFDataServicesAdapter] Configuring CFC adapter for destination cfcontact[LCDS-CF9]11/12/2009 12:23:15.528 [DEBUG] [Configuration] MessageBroker id: __default__ classLoader is: the MessageBroker's class loader and the context class loader (classLoader hashCode: 14529255 (parent hashCode: 9732842 (parent hashCode: 6635780 (parent hashCode: 2293223 (parent hashCode: 5035392 (parent system)))))[LCDS-CF9]11/12/2009 12:23:15.566 [INFO] [Configuration] Starting Adobe LiveCycle Data Services: 2.6.0.213876 Single CPU License11/12 12:23:15 user ServerCFCServlet: initServer coldfusion ready (startup time: 12 seconds)
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Configuration Files: Adaptersdata-management-config.xml > adapters <adapters> <adapter-definition id="coldfusion-dao" class="coldfusion.flex.CFDataServicesAdapter" default="true"/> <adapter-definition id="actionscript" class="flex.data.adapters.ASObjectAdapter" /> <adapter-definition id="java-dao" class="flex.data.adapters.JavaAdapter"/> </adapters>
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Configuration Files: Destinationsdata-management-config.xml > destination > cfcontact (example destination, abbreviated) <destination id="cfcontact"> <adapter ref="coldfusion-dao"/> <channels> <!-- <channel ref="cf-rtmp"/> --> <channel ref="cf-polling-amf"/> </channels> <properties> <component>samples.contact.ContactAssembler</component> <scope>request</scope> <use-accessors>true</use-accessors> <use-structs>false</use-structs> <!-- <hostname>localhost</hostname> --> <!-- <identity>default</identity> --> <!-- <remote-username></remote-username> --> ... <property-case> <force-cfc-lowercase>false</force-cfc-lowercase> <force-query-lowercase>false</force-query-lowercase> <force-struct-lowercase>false</force-struct-lowercase> </property-case> <metadata> <identity property="contactId"/> <!-- <query-row-type>samples.contact.Contact</query-row-type> --> </metadata> <network> <!-- <paging enabled="true" pageSize="50"/> --> </network>
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Config File Changes in ColdFusion 9When CF9 is configured for integrated LCDS, there are rearrangement of properties for ColdFusion channels and destinations: services-config.xml
<channel-definition id="cf-polling-amf" class="mx.messaging.channels.AMFChannel"> <endpoint uri="http://{server.name}:{server.port}{context.root}/flex2gateway/cfamfpolling" class="coldfusion.flash.messaging.CFAMFEndPoint"/> <properties> <polling-enabled>true</polling-enabled> <polling-interval-seconds>8</polling-interval-seconds> <coldfusion> <!-- define the resolution rules and access level of the cfc being invoked --> <access> <!-- Use the ColdFusion mappings to find CFCs--> <use-mappings>true</use-mappings> <!-- allow "public and remote" or just "remote" methods to be invoked --> <method-access-level>remote</method-access-level> </access> <!-- Whether the Value Object CFC has getters and setters. Set the value of use-accessors to true if there are getters and setters in the Value Object CFC. --> <use-accessors>true</use-accessors> <!--Set the value of use-structs to true if you don't require any translation of ActionScript to CFCs. The assembler can still return structures to Flex, even if the value is false. The default value is false.--> <use-structs>false</use-structs> <property-case> <!-- cfc property names --> <force-cfc-lowercase>false</force-cfc-lowercase> <!-- Query column names --> <force-query-lowercase>false</force-query-lowercase> <!-- struct keys --> <force-struct-lowercase>false</force-struct-lowercase> </property-case> </coldfusion> </properties>
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Runtime Dynamic ConfigurationDynamic options for Channels: Flex client side <mx:channelSet> <mx:ChannelSet> <mx:channels> <mx:AMFChannel id="myAmf" uri="http://myserver:2000/myapp/messagebroker/amf"/> </mx:channels> </mx:ChannelSet> </mx:channelSet>
Dynamic options for Destinations: BootstrapService in Java on server side
Con!guring components with a bootstrap service
To dynamically con!gure components at server startup, create a custom Java class that extends the "ex.messaging.services.AbstractBootstrapService class and implements the initialize() method of the AbstractBootstrapService class. You can also implement the start(), and stop() methods of the AbstractBootstrapService class; these methods provide hooks to server startup and shutdown in case you need to do special processing, such as starting or stopping the database as the server starts or stops.
•public abstract void initialize(String id, Con!gMap properties)
•public abstract void start()
•public abstract void stop()
<services> <service class="dev.service.MyBootstrapService1" id="bootstrap1"/>
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Assembler MethodsThe LiveCycle Data Management Service recognizes the methods:
Fill( ), Sync( ), Get( ), and Count( ) <--- Required
FillContains( ) <--- Optional, better performance
The !ll method retrieves records from a database and populates an array with the records. The get method retrieves a speci!c record. The sync method lets you keep track of synchronization con"icts by accepting a change list, which is an array of change objects. The count method returns a number that indicates how many records are in a result set.
To perform any of these database tasks, the Flex application calls the appropriate !ll, get, sync, or count method in the assembler CFC. You can also use a !llContains method, which checks whether to update the results of a !ll.
When you create your ColdFusion CFCs, you can do one of the following:
• Create an assembler CFC and a Value Object CFC.
• Create an assembler CFC, a Data Access Object (DAO) CFC, and a Value Object (VO) CFC.
You can place the database manipulation functionality directly in the methods in the assembler CFC and create a Value Object CFC, which is a CFC that contains property de!nitions and related get and set methods.
Or to separate the lower level database functionality from the high-level Flex assembler operations, you can create a Data Access Object (DAO) CFC that contains the lower level database functionality. Using this approach, which is the Bean/DAO methodology, requires that you place the !ll, get, sync, and count methods in the assembler CFC.
The methods in the assembler CFC call methods in the DAO CFC that perform the lower level database functions such as retrieving records. The DAO CFC creates Value Objects, which are CFCs that contain the values. A Value Object is essentially a row in the result set.
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Flex Project: LCDS / ColdFusionType: Web Application; Server: ColdFusion, LiveCycle Data Services
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
CFC WizardCreate RDS Connection, then Right Click on table in Dataview
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
CFC WizardChoose CFC Type: LiveCycle Data Services Assembler CFCs
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
CFC WizardGenerated for you: Assembler, DAO, VO CFCs; ActionScript VO
To Do: Write the main MXML for DataService UI
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
ColdFusion Assembler CFC
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Assembler Methods: Fill( ), Sync( ), Get( ), Count( ) required, and optional FillContains( )
ColdFusion Assembler CFC
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Assembler: Fill( ) returns array of CFCs (Value Objects). Calls DAO’s Read( )
ColdFusion Assembler CFCAssembler: FillContains( )
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
ColdFusion DAO CFCDAO: functions: Create( ), Read( ), Update( ), Delete( ) - Abstracted away from Assembler
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
ColdFusion DAO CFCDAO: query operations performed, converted to Array of CFCs (VOs)
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
ColdFusion Value Object CFCValue Object Pattern: Represents one row in table, with getter/setter methods
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Flex Client: AS Value ObjectActionScript VO: Maps to CFC VO. Abstracts record in table.
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Flex Client: DataServiceMXML with mx:DataService component (available with fds.swc from LCDS)
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Data Synchronization: ConflictsDataConflictEvent Handler: Similar to ResultHandler, FaultHandler Event may contain an array of conflicts For each one, use logic to determine who wins AcceptServer( ) or AcceptClient( )
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Data Synchronization: PaginationPagination with Data Management - client to LCDS Server Fill( ) fetches all records, cached in LCDS, sends pages to client on demand
<destination ...> <properties> ... <network> <paging enabled="true" pageSize="50" /> </network> </properties> </destination>
- server to datasource (not available with ColdFusion as backend) Custom Fill( ) fetches pages on demand - <paging enabled="false" pageSize="10" custom="true"/>
- implement new method signature in Assembler’s fill method as: fill(fillParameters, startIndex, numRows)
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Data Management Event GatewayNotify connected Flex clients that data was changed by server action (ColdFusion)
Using the Data Management event gateway type provided with Adobe ColdFusion, you can have ColdFusion applications notify Adobe Flex applications when data managed by a destination has changed. You configure the Data Management event gateway and write an application that uses the event gateway.
Notification actions: create, delete, deleteID, refreshfill, update, or batch.
The sendGatewayMessage( ) CFML function sends the message to the instance of the gateway. - Your CF code modifies the data set - You use the CF function sendGatewayMessage to issue a DM gateway message - The Data Management Event Gateway then notifies LCDS about the data changes - LCDS notifies all connected clients that care about the data
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Data Management Event Gateway
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Data Management Event Gateway
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Create new Contact as CFCCommit new Contact to Database with DAONotify Event Gateway with sendGatewayMessage( )
Simple LCDS Data Management DemoA “Hello World” type LiveCycle DataService example from beginning to end
- Flex Builder 3 - Flex SDK 3.x client side - ColdFusion 8/9 back end - Use CFC Wizard to generate code
ColdFusion Extensions with CFC Wizard not yet available for Flash Builder 4 (CF ER# 82729)
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Learn: Tour de Flex
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Learn: Books
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
http://www.slinnbooks.com/
Learn: LCDS Channel / Dest Designer
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
Create Channels and Destinations with this wizard (available with book purchase http://www.slinnbooks.com)
Learn: Online Presentations
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
See Charlie Arehart’s UGTV for directory listing of recorded presentationshttp://www.carehart.org/ugtv
BlazeDS and LiveCycle Data Services for ColdFusion 8 Tom Jordahl, Adobe
LiveCycle Data Services Christophe Coenraets, Adobe
BlazeDS and ColdFusion 9...Going Above and Beyond Chat Dave Ferguson
ColdFusion, LCDS, and Flex Magic Nick Kwiatkowski
Learn: Complex RelationshipsText
Steven Erat - LiveCycle DataServices for ColdFusion Developers - cf.Objective() - 2010
LCDS Data Management for ColdFusion Developers
Steven Erat
[email protected]: @stevenerat http://www.talkingtree.com/http://www.stevenerat.com/