The interview I did with Dzone at the Adobe MAX conference is now online!
In the interview we talk about a variety of topics, mainly centred about how and why to integrate ColdFusion and Java together, but also touching on things like Object Relational Mappers, including ColdFusion 9's integration with Hibernate.
Hope you like it, and please give it a nice 'up' on Dzone.
If you've kept and eye on the
transfer-dev mailing list, or possibly on
Brian Ghidinelli's blog
you may well have already seen this, but its an interesting development
with the next version of Transfer, so I figured it could use some
discussion.
If you have a look in the
Transfer SVN repository, you will see there is a new branch:
http://svn.riaforge.org/transfer/transfer/branches/pluggable_cache/
So the aim of Transfer's plug-able cache is to replace the cache that
Transfer uses with a extensible plug-in architecture. By default,
Transfer will ship with a
EHCache
implementation (which has some deployment limitations of CF8+ only, and
won't work on some shared hosts, depending on settings), and also a
ColdBox Cache implementation (once ColdBox Cache is released), for
wider compatibility. We are also looking at a CacheBox implementation
as well.
That being said, the sky is the limit - because the cache is now
plug-able and extensible, you can write your own cache providers for
Transfer, which can hook into almost anything you desire.
The reason behind this change is twofold. (1) The cache implementation
as it currently stood was not very flexible, and it meant that there
could only be limited extension to what was possible with that cache
and (2) there were certain implementation details within the cache that
could have been written a lot better (including a very random, rare,
and really hard to reproduce memory leak that only seems to show up
sometimes). So all in all, it simply made a lot of sense to move to an
already proven cache implementation, and take the burden off Transfer's
shoulders, while opening up caching to the developer community at the
same time.
Unfortunately, this means that if you had cache settings set up before,
you will now need to rewrite them with the configuration of choice of
the cache provider that is selected (or provided by default), which is
irritating, however the power provided by this new functionality is
worth it.
If you didn't have cache setting set up by default, then Transfer will
use EHCache as the default cache provider, with some basic settings
configured for you, and no changes will be necessary.
For example, now, a cache configuration will look something like this:
<objectCache>
<defaultcache provider="transfer.com.cache.provider.EHCacheProvider">
<setting name="config" value="/test/resources/ehcache.xml"/>
</defaultcache>
<cache class="none.Basic" provider="transfer.com.cache.provider.NoCacheProvider"/>
<cache class="none.Child" provider="transfer.com.cache.provider.NoCacheProvider"/>
</objectCache>
This sets up the default cache provider, as being the EHCache Provider,
and passes it a relative path to the ehcache.xml file that will
configure the EHCache.
You can also see that there are 2 class definitions for none.Basic and
none.Child, that use the NoCacheProvider, which, as you have probably
guessed, does no caching at all.
From there, we can configure our ehCache.xml file, which is quite straight forward.
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="600"
overflowToDisk="false"
/>
<cache name="AutoGenerate"
eternal="false"
maxElementsInMemory="10" overflowToDisk="false">
</cache>
</ehcache>
Here you can see that I've set up a defaultCache for all of the objects
being used by Transfer, such that it can have 1000 elements in memory,
objects can be idle in the cache for 120 seconds, but will be discarded
after 600 seconds.
I have also setup a cache specific to my class 'AutoGenerate', which
only allows 10 items to exist in memory at any given point in time.
There are many other options available for EHCache configuration, all which are covered in the
documentation.
If you are at all interested in writing your own Cache Providers, you will need to extend the
AbstractBaseProvider.cfc and implement all the virtual / abstract methods on there. It is actually pretty straight forward API.
There is something new that is required when bootstrapping Transfer now, which is the 'shutdown()' method.
A lot of cache implementation have 'shutdown()' methods, as they may
have to close connections to files, shut down threads, or let go of
certain resources before they can be garbage collected properly.
Therefore, for example, if you have Transfer in your application scope,
it is important to shut it down when the application ends:
<cffunction name="onApplicationEnd" returnType="void">
<cfargument name="applicationScope" required=true/>
<cfscript>
arguments.applicationScope.transferFactory.shutdown();
</cfscript>
</cffunction>
This will call shutdown() on all the cache providers Transfer is
using. In the case of EHCache, this is very important, as without it a
native thread will keep running, stopping EHCache from being garbage
collected.
If you need more control over your cache's you can access the native cache as it is exposed by the Provider, through the
Cache Monitor (whose API has changes slightly from before) methods 'getDefaultCache()' and 'getCache(className)' methods.Please have a play, and let me know how it goes on the
transfer-dev
list. I'm waiting on the other ColdFusion caches to become available
so everyone has a cache they can work with, before releasing this in a
1.2 release.
You can
download the full code samples I used in my Rapid OO talk that I gave most recently at
cf.Objective(ANZ) , and previously at
cf.Objective() .
Before people ask, I don't tend to post slides, as they are generally only pictures, and tend to have no context without me talking next to them.
That being said, I'll have a chat with the CFMeetup crew, and see if they would like me to give the presentation there, so there is a recording for posterities sake, and for those who couldn't make those conferences.
I can't believe it was well over a year ago I was sitting around with the steering committee of
cf.Objective() , and the conversation turned to 'What do you think of the idea of doing this conference in Australia?', it feels just like yesterday.
Now we're only
one week away from
cf.Objective(ANZ)!!!
If you haven't already
registered , there are still spots available, so make sure you grab them quick! You don't want to miss out on hanging out with super-top-programmers like
Justin McLean ,
Andrew Muller ,
Dan Wilson ... oh, and did we mention
Ben Forta and
Terry Ryan are coming? (like anyone missed that!).
See you all in a week!
While I am recovering from the Australian
WebDU conference, a few days before that started I got off a plane after the end of the wonderful
cf.Objective() conference.
I have to say, this year's cfObjective() was the best organised out of all the years I have been to. As per usual, the content was stellar, the hotel was lovely, and it was an absolute pleasure to catch up with everyone at the conference. I have to give a big 'congratulations' to Jared, Steven, Jim and the rest of the cf.Objective() crew for putting together such a smooth and professional conference.
I had the pleasure of doing two sessions,
Rapid OO Development with ColdFusion Frameworks, which covered a variety of techniques on how to increase your development speed when building OO models, and I was very happy to see that it seemed to have been very well received. I had one attendee let me know that 'Now I know
why I'm using ColdSpring! I was using it before, but now I know
why', which is an amazing thing to hear as a presenter, that you've managed to create an 'Aha!' moment for someone.
I also did my
Introduction to Building Applications with Transfer ORM, which was a repeat of the session that I did last year. Unfortunately
Ray Camden couldn't make it to do his
Transfer session, so I was called in at the last minute to take his place.
The big news that we announced at cf.Objective(), is that I will now be the lead developer on the
ColdSpring project. Since Chris Scott's major focus these days is the Swiz Flex framework, he decided it was time to pass on the reigns, and since I tend to talk to him regularly about Cold/Spring, have contributed code to ColdSpring , and know about running an Open Source project, he seemed to think I would be a good fit. I'm pretty excited about the opportunity, and have discussed some great ideas with theColdSpring development group, of which Chris is going to stay on as lead architect. I expect we will start off by building the infrastructure around the project, e.g. a centralised wiki, ticket tracker etc, and then move on to some more interesting items.
The obvious question there is, of course, what does this mean for Transfer? (I think I need to start writing down how many times I've been asked if it's 'Dead'. Does anyone actually expect a 'yes' for an answer?), and quite frankly, I don't see this impacting on Transfer much at all, simply because this is going to be code that I would have probably ended up writing on ColdSpring anyway, but it is now a more formalised relationship. When I run into a feature or a bug on an Open Source project, that I want to be implemented, my first natural reaction is to start looking into the code, and writing the feature. This was first exemplified by my contribution toColdSpring of
annotation based pointcuts. There are several aspects of ColdSpring I wanted to improve on, so it was just a natural reaction for me to end up writing code for it.
As stated, the content at cf.Objective() was brilliant as per usual, with my own personal highlights being, Advanced ColdFusion Server Administration (Adam Lehman), Advanced ColdFusion 9 ORM (Terry Ryan) and ColdFusion Portlets (Adam Haskell).
Thinking about the content, I have a little confession to make, that I realised on the way back from cf.Objective() this year. I have a tendency to go to the wrong sessions when at a conference. This may sound like a weird thing, but I realised the last few years I tend to go to sessions that I already know a lot about, just to see if they say something a little bit extra that I can add to my knowledge base. Quite often I end up walking out feeling like I haven't added much to my repertoire. Really what I should be doing is going (mostly) to sessions in which I know
absolutely nothing about, which means I actually get the best return on the my investment in the conference. While it may not be specifically applicable to what I'm currently doing, at the very least it will inspire me to do some interesting new things, and may give me some knowledge that I can then apply at some point in the future. This is a philosophy I plan on applying to all future conferences that I attend.
Finally, I also had the opportunity to be part of a
CFConversations round-table on the second night of the conference.
Brian Meloche,
Andy Powell,
Andy Matthews and I had a really good chat about the conference in general, our thoughts on some of Adobe's upcoming products, various other topics relating to ColdFusion. It was lots of fun to do, and you can download and/or read more about it here.
Again, thanks to all the cf.Objective() crew, and look forward to seeing many of you again at cf.Objective(ANZ).
For a while now, Luis Majano and I have been working on CodexWiki , a ColdBox , Transfer and ColdSpring powered wiki.
The features in it have come together really nicely, including one of the slickest wiki editors I have ever seen, and it has finally gotten to the point where there was no point in keeping it in private beta any longer.
If you are interested in checking out go to http://www.codexwiki.org , and if you wander over to the ColdBox blog , you can see some great screenshots.
Its been great working with Luis, and we have many more ideas for this wiki in the coming future! Watch out :oD
I officially closed the
Transfer
survey a short time ago, as results had slowed to a crawl. Overall we
had 87 respondents, and it was really, really useful to get the
feedback.
I can honestly say the feedback from this survey has changed the road-map for the next two releases of Transfer.
Transfer Experience
This was a really interesting set of questions. 41% of respondents say that they use Transfer
every day, which is really exciting. Only 1 person responded with
What is Transfer?, which hopefully means I'm doing a decent job of getting at least the name 'Transfer' in people's knowledge sphere.
Most
people have been using Transfer for only 1-6 months (30%), while coming
second place (26%), 6 months to a year, and third 18% for 1-2 years.
Only 2 people have been using it for more than 2 years, but since the
project has only been around for 3, and the first year, itwasn't all that stable, I'm very happy with these results.
The version of Transfer that people are using totally shocked me!
73% of
people where running either 1.1 (which at the time was RC), or the BER
release of Transfer. 36% of people were running 1.0. Only 3 people were
running 0.6.3, which is a good thing, as it was a really buggy
release. I'm so pleased to see the adoption rate is so high!
Other ORMs people have used
This
was another interesting section, in that Reactor scored the highest at
51%, and Hibernate came in second at 21%. Interestingly enough, 31% of
people had
never used an ORM before Transfer.
Transfer Enhancements
Not surprisingly, 58% of people that that Performance improvements were most important. I wasn't
surprised by this, and I expect this won't change no matter how fast I
make Transfer. The annoying thing is being constrained quite severely
by how fast ColdFusion can createCFCs, which isn't nearly as fast as I would like.
Several comparisons where made between Hibernate (and other Java/Groovy ORMs)
and Transfer, and while it is understood that Java will always be
faster, they still want comparative speed out of Transfer. Not sure
how I'm going to get that to happen, but I know I will try my best.
To quote a survey taker:
'...the
whole thing about not really being able to instantiate collections of
associated objects with decent performance - therefore you do your head
in constantly switching between object-think and query-think. This is
the #1 reason I'd choose Java over CF for a decent-sized domain layer.'
The
fact of the matter is, he is right on what he's saying, and I hope its
something that Adobe listens to with future versions of ColdFusion.
Interestingly
enough, there were several requests specifically to be able to retrieve
arrays of Transfer generated Objects. I had purposely avoided it due
to performance concerns, but I think this may be something that will
end up being discussed on the Transfer mailing list further, as there
were a few people who were interested in this feature.
Integrated
Flex support was another hot topic. I'm currently in the process of
learning Flex at the moment, so expect to hear solutions for this
common problem around the corner.
DB introspection was a
huge draw card, with multiple comments like '
...config file generation without using illudium...' and '
...non-generative XML can be burdensome in large applications...',
which is all true. In the next few releases we'll be looking at doing
property auto-generation from the database, and also providingDDL support for Transfer to generate your database for you.
Inheritance
mapping was also a big feature request (far more so than I had
originally ever thought it would be, and this is even before the
recent spate of
ORM
articles), and got me thinking about it a whole lot more. To that
effect, expect to see Inheritance mapping as one of the next big
features in the next release of Transfer.
Some other interesting requests that came through were:
- Annotations in Transfer XML data
- Railo support
- Generate methods for setting object properties from a struct
- Query paging / Query limits
- Bidirectional M2M compositions.
- TQL aggregates
Support and Documentation
Support
seems to be well received, with 46% of people giving Transfer support a
4, and 30% giving it a 5, where 5 was 'Very useful' and 1 was 'Not
Useful'.
That being said, ease of learning could be better, as
on a scale of 1 (Easy) to 5 (Really hard), the majority, 43%, sat in
the middle at, 3, although 34% gave it a 2.
From reading
comments, the big element to help Transfer learning, is examples,
examples, and when done, some more examples. Comments include: '
Beginner Tutorials, step-by-step type. Maybe even videos...', '
Lack of tutorial type documentation. Especially for advanced topics.', '
The examples on the site are a bit limited...', '
More examples, and more advanced examples...',
so the pathways are pretty clear. I will be contacting members of the
Transfer community and contacting people who participated in the survey
to help with this.
Conclusion
I'll be finalising the road-maps for the next 2 releases of Transfer based on this feedback, however, I'm looking at moving to Skweegee from Project Tracker, so once I've made a decision in that area, and how to manage previously completed tickets and milestones, I'll get stuck in.
Thanks to all who participated, it was really useful to get the feedback!
Just a reminder that I'm doing an Adobe eSeminar tomorrow at 2pm on Transfer Caching Mechanisms
One of the most powerful features of Transfer is its highly
configurable, in-built caching layer that allows for significant
performance gains for a given application when configured correctly.
During this eSeminar we'll discuss caching concepts such as
'caching algorithms' and 'memory sensitive caching', so that as a
Transfer Developer, you'll have a better understanding of the
intricacies of Transfer.
Click here to register
Click here for World Times
After a few minor delays, Transfer 1.1 Final is ready for final release.
A few bugs and fixes occured during the release candidate period, so make sure you upgrade to the final release if you are running the release candidate.
For those of you not familiar with the new features of 1.1, some of the highlights of 1.1 are:
Huge Performance Enhancements!
During Testing (on my machine), a 500 Object load has been reduced from an average of 8 seconds down to an average of 6 seconds. This is a speed increase of around 25%!. I've even seen Object loads of 500 objects as small at 2 seconds.
Even for the performance alone, it is worth upgrading!
Transfer Object Proxies
This of this like Lazy Loading, but on steroids!
With a simple new setting 'proxied' on onetomany, manytoone and manytomany elements like so:
<onetomany lazy="true" proxied="true">
...
<onetomany>
Objects in the collection will be loaded as Proxies of the real object, and their underlying data will be loaded on an individual basis upon request.
A very handy feature when dealing with large collections.
New Discard Algorithm
Before, if you had a configuration when A -manytoone->B, and B was discarded from the cache, both A and B would have been discarded.
Now in the new algorithm, if B gets discarded, A simply unloads its many to one, and stays resident in the cache. This ensures there is less unnecessary too-and-fro between the database and Transfer's cache.
Cache Monitoring and Reporting
While there is a new CachMonitor component that allows you to get fine grained reporting on what the cache is doing, to get a re-built cache report, it is as simple as:
<cfimport prefix="report" taglib="/transfer/tags/reports">
<--- Basic Report --->
<report:cacheReport monitor="#application.transferFactory.getTransfer().getCacheMonitor()#">
<--- Detailed Report --->
<report:cacheReport monitor="#application.transferFactory.getTransfer().getCacheMonitor()#" mode="detail" chartsize="300">
TQL Custom Tags
Big thanks to Elliot Sprehn for contributing these TQL custom tags, that make it super easy to do TQL queries, in a <cfquery> style!
For example:
<cfimport prefix="t" taglib="/transfer/tags">
<--- Do list operations --->
<t:query name="result" transfer="#getTransfer()#">
select
u.firstName, u.lastName
from
user.User as u
where
u.email like <t:queryparam value="%example.com" type="string">
</t:query>
<--- Do read operations --->
<--- Get a single record as a TransferObject --->
<t:query name="user" action="read" class="user.User" transfer="#getTransfer()#">
from
user.User as u
where
u.email = <t:queryparam value="user@foo.com" type="string">
</t:query>
Lots of bug fixes
The usual slew of bug fixes, improvements, and other various adjustments.
If you want more information on what got included in this release, and why to upgrade, I would highly recommend the 'What's new in Transfer 1.0 (and 1.1) ' presentation recordings, and also make sure you check out the Release Notes .
Transfer 1.1 download .
I've put together a short Google Forms survey on Transfer to get some consolidated feedback from the community at large on what people are doing with Transfer, what features they want in the future, and what can be done to improve it overall.
It isn't limited to just people who are using Transfer. If you are not, I also want to hear your reasons why, as hopefully it is something that can be improved upon in the future.
It shouldn't take you much longer than around fifteen minutes to complete, and I would really appreciate the feedback.
Transfer Survey
I'm feeling pretty excited, because my first article for Adobe DevNet has finally been published.
An introduction to ColdFusion frameworks
It covers the differences between Model-View-Controller, Dependency Injection, and Persistence frameworks, and also gives a general overview of some of the most popular offerings that are available in ColdFusion right now.
There was a bit of a rush towards the end, and some pieces of it are not as perfect as I would have liked, but I hope that it serves as a way for people to enter into the ColdFusion framework world, gives them enough information so that they understand the differences between framework types, and also allow them to make an informed decision on which one to use based on their personal preferences and needs.
I'm doing a repeat of the What's new in Transfer 1.0 (and 1.1) Talk at cfmeetup.com tomorrow!
WHEN
Thursday, September 18, 6:00pm US EDT (UTC/GMT-4) (What time is that for you? See http://www.timeanddat...
which shows the time as US EDT and you can choose your city from the
list offered to see what time that is in your own timezone.)
AGENDA
After a long wait, the Transfer 1.0 release is finally here! This
release comes bundled with lots of brand new features, bug fixes and
performance improvements.
This presentation will give you a good
overview of new functionality the 1.0 release brings to your developer
tool kit, providing you with even more ways to implement your
ColdFusion applications faster than ever before.
We will also have a sneak peak at some of the 1.1 features, that are currently in release candidate 2.
In case you missed the first talk, you can now catch it again!
Release Candidate 2 of
Transfer 1.1 is now available for download.
It has several critical bug fixes from the previous Release Candidate.
While I did promise several blog posts about the new features, I simply
haven't had time, as I'm been incredibly busy with client work, and
working on Transfer.
That being said, the documentation for all the new features has been written in the
wiki, and you can also view the
latest presentation recording that I did, which covers many of the new features found in Transfer 1.1.
Transfer 1.1 Release Candidate 2 can be downloaded
from here.
Just to let you guys know, the recording for the recent eSeminar, What's new in Transfer 1.0 (and 1.1) is available .
You will also find the code examples available to download along with it.
It was a fun presentation to do, so I hope you enjoy it.
Tom de Manincor Released yesterday TransferSync , a nice tool that works with Transfer to enable it to be used across a cluster of servers, and maintain its cache.
It's really a nice little piece of software, a huge thanks goes out to Tom for putting this together. He's expanded on some of the ideas that Sean Corfield had in his original clustering implementation, and has expanded them nicely.
For more details on TransferSync, check out Tom's blog posts !
I'm going to be presenting 'What's new in Transfer 1.0' in Adobe's continuing eSeminar series.
Agenda:
Transfer 1.0 is finally here! This release comes bundled with lots of brand new features, bug fixes and performance improvements. In this eSeminar we'll introduce you to the new Transfer functions and provide more ways to implement ColdFusion applications faster than ever before.
We may also look at the new features that are in 1.1 at the same time as well.
Time and Date:
Melbourne, Australia: 2pm, Friday 15th August
USA - PST: 9pm, Thursday 14th of August
USA - EST: 12am, Thursday/Friday, 14th of August
London, UK: 5am, Friday 15th of August
For more times in your timezone, see: http://snipurl.com/3fk87
To register, go to:
http://www.adobeeseminars.com.au/events/register/18103633
Look forward to seeing you guys!
Transfer 1.1 Release Candidate
Hot on the heals of
Transfer 1.0, is Transfer 1.1, now with added sugar and spice, and all things nice! :
oD
This
release has been squarely aimed at large Transfer based systems, in
order to give them more control over the caching layer, and provide
more performance overall. (Which is not to say this isn't a release
for everyone else as well, because there are lots of good things to
share all around).
I'm not going to go into
huge detail on each of the new features, but instead will be running a
series of blog posts, highlighting each of the new main features and
how you would use them over the next coming week or so.
The
documentation is still mostly forthcoming (have to get that paying work
finished!), but expect it to show up on the wiki in the next few days.
If you want a head start, most of the tickets linked in the release
notes have a link to the relevant google group post, or the usage
details can be found in the tickets themselves.
To give you a taste, of some of the really interesting new features:
- A performance upgrade of at least 25% in object loading!
- Introspection and statistics for the caching layer
- A new cache discard algorithm, that allows granular control over the cache
- New cache time-out settings
- The ability to load manytoone, onetomany and manytomany collections as TransferObject Proxies, giving you a way to manage large object collections.
- Custom tags for TQL (Big Thanks to Elliot Sprehn!)
- Plus more!
So I figured I better head this off at the pass, because I'm already getting questions in my inbox about whether this is the end of the road with Transfer , and what I plan to do with it, and OMGZ! TRANSFER IZ DEADZ!!!.
Let it be known, it couldn't be farther from the truth.
Transfer is not dead, development will still continue way into the future, and I can see a healthy life-cycle for its continuance.
A few points for your consideration:
- ColdFusion 9 is not even here yet. Nobody even knows the exact date it will be shipped. You want ORM? You can have it right now with Transfer! No waiting around, no fussing. The documentation is written, the example applications are there, and you already have a large community to integrate with.
- Hibernate integration may not even be implemented with ColdFusion 9. Yes, we've seen some short demo's, but we've seen demo's of functionality in pre-release at keynotes before, and they didn't make it into production before. Hibernate and ORM is a pretty complex beast, and especially tying that into CFCs, so any number of things could make it ship late, or not at all.
- ColdFusion 9 will cost you $$$ to upgrade. So you're not getting all this for free. Transfer can be used right now for $0, and will always be $0. It is only then up to you whether you want to pay for support, or training, consulting or even new features!
- The Transfer release cycle will always be faster than ColdFusion's. If there is a feature you want in CF's ORM support, you'll have to wait ~18 months. The Transfer release cycle is around the several month mark, and with the sponsored development program, the features you want in your ORM support can generally be developed in the same week you request them in, in fact the last sponsored development I completed I did in 6 hours! (Yet to be blogged, although in SVN).
- We don't know how well the ORM integration in ColdFusion 9 will be developed. While I love the hard work that Adobe does, we can all remember Flash Forms. Lots of Shiney, not very useful (maybe that was too low a blow? ;) ). My point is, there is no point in putting the nail in the coffin until we really know what we are dealing with.
- There are plenty of businesses and projects out there using Transfer already, and there is no reason they would suddenly stop using it, and switch (although that is a possibility). As long as people keep using Transfer, I will keep developing it.
- Transfer is a proven technology that has undergone a lot of rigorous testing. While Hibernate can say the same, we have yet to see how the ColdFusion and CFC integration will perform.
- There is no reason why Transfer can't take advantage of some of the ORM integration tools. For example, if a CFC annotation structure is setup for use with Hibernate, there is no reason Transfer couldn't use the same annotations, so using one tool or another is quite seamless.
- All in all, competition is a good thing. Having competition forces everyone involved to strive to become best in breed. So this will actually be a good thing, both for Transfer and for ColdFusion.
I don't want to be showering doubt over the ColdFusion 9 integration with Hibernate, it makes sense for them to do it, and I can completely see where it is coming from, and there are a lot of smart people behind it. But, there is still a lot of unknown factors here, and a lot of reasons to still use Transfer, so don't feel like the project, or the business is going to die, because its not.
I'm really excited by some of the announcements I've been seeing with ColdFusion 9, including the ORM integration, and I think the next few years will be an exciting time for the ColdFusion community.
This is just to post a link to the recording of the session of Introduction to Building Applications with Transfer ORM that I did recently on cfmeetup!
To view the recording, you can go here .
This one should hopefully not have the sync issues the previous one did.
Today is the day in which
Transfer finally hits its stable, final, and complete 1.0 status.
The release candidate phase is finally over, and it showed up some critical bugs, which have since been fixed.
Things are moving along speedily, with the
recent completion of the support contracts, and sponsored development programs.
I just finished writing a
day's training program for
webDU, which will soon to be available both on-site, and via Connect.
On
the next to-do list, is the rebuilding of the Transfer and Compound
Theory websites, with alerts for events, training, and a whole lot more!
I'd like to extend a huge
thank you to the community
that surrounds Transfer, you guys are fantastic, and without you there
is no way Transfer could be where it is now.
Keep expecting good things from Transfer!
You can download the 1.0 Release from
here.
For more details, check out the
Release Notes.
Tomorrow, I will be presenting my Introduction to Building Applications with Transfer ORM presentation, that I gave at cf.Objective() on coldfusion.meetup.com.
This will happen at:
USA EST: Thursday, June 5, 2008 at 6:00 PM
USA PST: Thursday, June 5, 2008 at 3:00 PM
Australia: Friday, June 6, 2008 at 8:00 AM
London: Thursday, June 5, 2008 at 11:00 PM
More details can be found at: http://coldfusion.meetup.com/17/calendar/8035918/
See you all there!
Announcing two new developments for the Professional Open Source Software side of
Transfer.
Compound Theory is now offering the following new programs:
Transfer Support Contracts
The basic yearly support package provides you with 20 hours of support, covering:
- Installation
- Usage
- Performance
- Bug Fixes
Contact through several different channels, including:
- Email
- Skype
- Instant Messenger
Extra support hours can be purchased at any time, and rolled over into subsequent years if required.
Sponsored Feature Development
If there is a feature that has yet to be developed for Transfer that your project could really use, either from the
extensive enhancement ticket list, or something that you have thought of, it is possible to sponsor its development.
Full
or partial sponsorship is available, and will give the feature you
desire priority in its development, and/or the ability to specify the
schedule it needs to be developed in.
If you are interested in either of these programs, or want more details, please feel free to contact me for more details
through the contact form, mark [at] compoundtheory [dot] com, or through Skype through the account mark_mandel.
There have been several bug fixes and performance and memory usage
improvements since the initial Release Candidate, so here I present to
you Release Candidate 2!
Transfer 1.0 Final is slated for the 8th of June, just a few days before webDU is going to start!
In case people haven't realised yet, but there will be a
full day's training for Transfer at webDU, in which we're going to do a lot of hands on coding, so you can get a really good understanding of the Transfer framework.
The Transfer support contracts I
blogged previously should be ready this week, we're just finalising some final details, so please
contact me, if you have any interest in this area.
Also, just a quick reminder that Transfer based consulting services are also available right now, so feel free to
contact me if you have any needs.
The Transfer 1.0 Release Candidate 2 can be download from
here.
Release notes can be found here .
Okay, forgive me one Australia joke ;o).
cf.Objective()
was an absolutley fanstastic conference all around, and I had an
incredible time. Not only were the presentations top notch, but it's
always an incredible pleasure to meet up with the people that I only
tend to see once a year, and I always get a huge burst of inspriation
just being around clever people and sharing various ideas over a drink
or two.
Presentations
I'm not going to go into all of the presentations, but as per usual,
they were all of an incredibly high calibre, and I came out of all of
them learning something new.
Highlights of the conference for me were:
Model-Glue 3: Back to its Roots - Joe Rinehart
This was a really interesting presentation, to see what MG3, code named
Gesture,
had in store for its users. I really like the innovative approach Joe
has taken to enable to framework to generate itself as you develop with
it.
Selling Professional Development at a Hostile Shop - Terrence Ryan
I
now refer to Terrence as 'the master manipulator' ;o). He outlined a
series of personality types that can often occur when working in an
organisation that tends to lean against the utilisation of software
development practices such as frameworks, unit testing, or version
control, and how to encourage them to accept, and even appreciate,
these practices when previously they had shunned them.
His
use of images to illustrate each of his points was also particularly
clever, I never knew that a photo of Bea Arthur wrestling a velociraptor could ever have any sort of context!
Workshop: ColdSpring 1337 - Chris Scott
Honestly,
this was probably my favourite session at cf.Objective(). Chris went
through some really interesting way you can use AbstractFactories and
AOP to really push what is possible to do within ColdSpring. He used a
Flex / ColdSpring / Transfer/ Yahoo Maps mash-up to show this off,
incorporating some nice Transfer powered Flex Remoting, implemented
with some very nice ColdSpring Remote Proxy AOP work (Before people
ask, yes, this will be finished off and released at some point)
Finally, Chris showed off his new Flex framework,
Swiz,
and while I don't even do much (any?) Flex development, I looked at and
just went 'Well, that is a pretty sweet framework'. I'm looking to do
some Flex work in the near future, and I can see me really getting into
Swiz.
Workshop: Advanced Techniques with the ColdBox Framework - Luis Majano
While
I didn't attend most of this presentation (I think I was balled up on a
couch somewhere trying not to drop off to sleep), I dropped in at the
end, so that Luis and I could do a quick announcement of the CodexWiki
Open Source Wiki, which is currently powering
docs.transfer-orm.com. We are opening a private beta for Codex, before we do a full release. If you are interested in being involved, feel free to
drop me an email.
Transfer
One of my favourite things about the conference was running around
giving lots of people Transfer stickers. It gave me a wonderful
opportunity to talk to lots of people about Transfer, and I think I
actually managed to get it so that about one in every third person had
a Transfer sticker on their laptops (Statistics based on no real
analysis)! I passed on a stack of stickers to a few people, so if you
weren't fortunate to get any at cf.Objective(), or couldn't attend, you
may find some people around who still have some to hand out.
I also did two presentations on Transfer, one of of which was a repeat. While the first time I presented
Introduction to Build Applications with Transfer ORM,
didn't quite go according to plan (technical difficulties), people
still seemed to get a lot out of it, which I was very happy about. The
Transfer ORM Caching Mechanics and the repeat of the
Introduction talk went far more smoothly, and got good reviews from the
people that I talked to, which is very pleasing.
ColdFusion 9
The ColdFusion 9 keynote, and BOF was another highlight of the
conference for me, although, I must admit, I didn't hear any feature
requests that really surprised me.
Adobe is further opening up the ColdFusion development process, promising us a Open Bug Tracker, and setting up an Advisory Committee, which is fantastic.
We
got a hinting at a ECMAScript (style?) syntax for ColdFusion
components, which I know is something that people, myself included,
have wanted to a long time. From that, there seemed to be a big push
to be able to write AS3 on the server side. Considering that a lot of
new CF developers seem to be coming from Flex, I think this would be a
really smart move on Adobe's part. Not only does it streamline the
training process for Adobe based Rich Internet Applications, it
provides a solid, single language for Adobe products, which can then
only be expanded.
That being said, it
would be very important that the CFML language also be kept intact,
both for backward compatibility, and for the fact that a tag based
syntax just makes so much sense on the view layer. (Oh, and let's not
forget, some people just like writing CFML ;o) )
People
One of the biggest draw cards for cf.Objective() is the people you get
to hang out with. For me personally, it's the only chance I get per
year for me to actually see a lot of the people that I speak to day in,
day out on-line.
It also gave me a chance to meet and talk to some of the people I've worked remotely with as well, specifically, the
Dinowitzs, who run the great
Fusion Authority Quarterly Update, and the really cool
Alagad crew.
Let's also not forget that I won the Wii,
which was a big surprise! It was very amusing watching multiple people
try and convince me how it wouldn't work in Australia, and that I
should just give it to them, because really 'I didn't need it' ;o).
Let it be known, that a new power cable is on its way in the post, and
soon theWii will be up and running smoothly.
Oh yeah.. and I don't care what you lot say, it's cay-shing, not caaashing. ;o) See you all next year!
Wow. When I started this project
back in 2005
I had no idea it would blossom into what it is now, or that it would
take me 3 full years to turn it into an actual 1.0 release.
It's been a crazy, interesting, frustrating and incredibly rewarding ride, and I plan on continuing it well into the future.
So here I present to you the release candidate of
Transfer 1.0, ready for your
download and consumption !
Some of the major new features include:
- Composite Key Support
- Transaction Support
- Binary Data support
- Configuration includes
- Cascading operations e.g. cascadeSave(), cascadeDelete()
- A huge number of performance improvements
- Public Bug Tracker
- Public Wiki
Please see the
full release notes for more information.
I want to extend a big thanks to all those people who have helped out
with Transfer, with code, testing, documentation, or just giving your
ear as I try and work out a n-th level nested recursive threading
issue, you all are too many to mention, but you know who you are, and
you guys rock!
I'm really happy with the way the Transfer community has grown over the
past few years, recently passing 320 members, and big kudos to you all
for helping me bring Transfer to this 1.0.
In the coming months, the following is the plan for Transfer -
- Transfer Support finalised and advertised.
The details of this have been worked out, expect a blog post on this either during, or shortly after cf.Objective()
- Infrastructure
You will notice there is the new Wiki and Bug Tracker. There will be a
complete rebuild of the Transfer site, to integrate the wiki and the
tracker, and provide the community with more ways to learn and interact.
- Transfer Training
This is the next big thing for Transfer, and I will be starting to write the curriculum after I get back from cf.Objective()
- Transfer Survey
Expect to a see a survey in the upcoming months, to get a feeling for
how the community is using Transfer, and what sort of enhancements they
want for the future.
- Transfer 1.1
Yep, I've already started thinking about a 1.1 release! I think I also
know what new features will be in it, but I won't ruin the surprise.
- Transfer Developer and Training Certification
I've had some recent interest in this, and it is still on the roadmap. Once the training curriculem is finalised, this will be also be developed.
Hopefully that will give you guys something to think about while you play with the 1.0 Release candidate!
The recording for the recent Transfer eSeminar is now available to be viewed at your discretion.
The same code can also be downloaded from here.
There is a small silent part at the beginning due to the fact my Windows VM crashed, but the rest was relatively smooth sailing.
I hope you enjoy!
This is actually the same presentation I will be giving at cf.Objective() , so you may want to hold off on watching it, and catch it live instead ;o)
Big thanks to the Adobe Pacific eSeminar series for allowing this to happen. It's worth checking it out to see if there is another eSeminar that is going to interest you!
Just a quick reminder that I will be doing an eSeminar for Adobe tomorrow, 25th of April, at 2:00pm Melbourne, Australia Time.
Agenda
Introduction to Building Applications with Transfer ORM
When developing an Object Oriented web based application, it is normal to have a database with relational tables and a series of objects that represent that data. Often, the amount of time and effort it takes to manually map these objects back and forth from a database is large, and can be very costly.
Object Relational Mappers (ORM) were developed to cut down the amount of time this process takes, and automate the translation between a relational database and an Object Oriented system.
Transfer ORM's main focus is to automate the repetitive tasks of creating the SQL and custom CFCs that are often required when
developing a ColdFusion application. Through a central configuration file Transfer knows how to generate objects, and how to manage them and their relationships back to the database.
This presentation will outline the basics of what an Object Relational Mapper is, the use case for using one within web application
development, as well as taking a code centric, step by step view of how to install, configure and use the basic functionality of Transfer ORM.
Times
For those Aussie folks - yes, this is Anzac day! (Yeah.... I dunno why I was given a public holiday, go figure) ;o)
For those not in the AU region, the time conversion is:
USA - EST: 12am, 25-26 of April
USA - PST: 9pm, 25th of April
UK - London: 5am, 25th of April.
If you feel like staying up / getting up early, you are more than welcome to join us.
This is the new Introductory presentation I will be giving at cf.Objective() this year, so you can come by and get a preview of what I will be speaking about.
Registration
http://events.adobe.co.uk/cgi-bin/register.cgi?country=pa&eventid=6503&venueid=6858
As you may have picked up the
occasional teaser, the Transfer documentation has been officially moved to a new Wiki, which can be found at
http://docs.transfer-orm.com.
The old URLs now redirect to this link, and the project page links are also pointing here.
This is one of the new faces of the soon to be released
Transfer 1.0, a large part of which is project infrastructure.
In
the next few months, you will see more and more being built, to help
build the Transfer community, and provide it with the support it needs
to help make Transfer into an even better project than it is now.
As per what is CodexWiki? Well, I guess you'll have to come to
cf.Objective() to find out...
I just had a really nice experience with NovaHost today, so I felt like sharing the love.
Novahost sponsors the ColdFusion hosting for CompoundTheory, and also for Transfer-orm, and for that I'm always very, very grateful.
Today, I needed some aspects of this hosting changed, so I jut popped
them a quick Instant Message via MSN, and was quickly handled, without
any problem at all. I just love it when Customer Serivice just works!
If you are interested in shared ColdFusion hosting in Australia, click
the ad on the right hand side of this post. They're reliable, easy to
deal with, and are run by people who understand ColdFusion (which for
me is the big selling point).
Okay, I'll stop being gushy now, but seriously, check them out ;)
I have to say, I'm very excited about this years webDU,
the speakers looks fantastic, and the topics seem to be really great as
well! I'm particularly excited by the fact that two of the ColdFusion engineers will be there, and presenting!
On the Transfer side of things, I will be providing a full day workshop on Day 0 of webDU,
so for those of you who are keen to gets started with Transfer, but
haven't before had the chance, or are simply looking to brush up on
your Transfer skills, now is the perfect chance.
Otherwise, I will also be presenting Database Queries the Easy Way using Transfer Query Language where I will cover the hows and why of TQL, and some of the ways it can help you write even less SQL.
Yes, I know, I broke my own feature freeze... I'm a very naughty
developer. I had a need for Transaction support in the code base I've
been writing for the new
Transfer
documentation, so I figured that I had to simply write it. That and I
thought it was a super cool idea, so I couldn't let it slide ;o)
The Problem
There are two issues I was trying to solve with Transfer Transaction
support, so let's look at a common Transfer Transaction scenario and
see what the problems are.
Say we have a TransferObject named 'Foo', and we have a Service, named 'FooService'. We may have some code that looks like:
<cffunction name="saveFoo" hint="saves Foo, and its children" access="public" returntype="void" output="false">
<cfargument name="foo" hint="The foo" type="Foo" required="Yes">
<cfset var children = arguments.foo.getFooChildrenArray() />
<cfset var child = 0 />
<cftransaction>
<cfset getTransfer().save(arguments.foo, false) />
<cfloop array="#children#" index="child">
<cfset getTransfer().save(child, false) />
</cfloop>
</cftransaction>
</cffunction>
In which we save Foo, and its many FooChildren, and we wrap it in a
<cftransaction>, so that if anything goes wrong, the whole set of
data gets rolled back.
Now, there are two issues with this scenario that need to be addressed:
Nested Transactions
You can't nest the saveFoo() call inside another Transaction block. i.e. code that looks like:
<cffunction name="saveFooParent" hint="saving a Foo Parent" access="public" returntype="void" output="false">
<cfargument name="fooParent" hint="The foo" type="FooParent" required="Yes">
<cftransaction>
<cfif arguments.fooParent.hasFoo()>
<cfset saveFoo(arguments.fooParent.getFoo(), false) />
</cfif>
<cfset getTransfer().save(arguments.fooParent, false) />
<cftransaction>
</cffunction>
This
will throw an error, as ColdFusion won't allow you to nest
<cftransaction> blocks. We could write something similar to what
Transfer already does, and pass a boolean to the save method to tell it
whether or not to use an inner transaction, but this is cumbersome, and
depending on your application architecture, you may not be in a
position to know if a given method is in a transaction.
Cache Synchronisation
Cache synchronisation is a real problem when database data rolls back, and the Transfer cache stays the same.
i.e.
If we look at the saveFoo() method above, if the data on Foo has been
updated, but something goes wrong when saving the children, then the
data for Foo gets rolled back along with everything else, but the cache
stays the same, which can be a very bad thing, as your object data is
totally out of sync with your database (and not when you want it to be).
The Solution
So what is the solution? The solution is to use the new
Transfer Transaction Object!
The Transaction object provides both:
- Nested Transaction support.
If
a Transaction wraps another Transaction, the inner transaction just
becomes merged with the outer, as if it was just one big Transaction.
- Transfer cache synchronisation
If
anything goes wrong in your Transaction, then Transfer will
automatically discard any object whose data was modified during that
Transaction.
So let's look at how we can use the
Transaction object. First of all, how do we get it? Very simply, it's
available from the TransferFactory, so can now go:
<cfscript>
transaction = application.transferFactory.getTransaction();
</cfscript>
And
get access to the Transaction object. On a side note, the reason it is
accessible from the TransferFactory, and not Transfer, is because it is
not specifically tied to Transfer. You can use this Transaction object
with anything that requires a <cftransaction> wrapped around it.
There
are three different ways you can use the Transaction object, and will
look at them all, in regards to the example we gave above.
Direct Execution
The first one is the simplest:
transaction.execute(component, methodName, [arguments])
This simply executes a given method (even private ones!) on a given component, with an optional struct of arguments.
In the instance of above, we would have to change our code to look something like:
<cffunction name="saveFoo" hint="saves Foo, and its children" access="public" returntype="void" output="false">
<cfargument name="foo" hint="The foo" type="Foo" required="Yes">
<cfset getTransaction().execute(this, "_saveFoo", arguments)>
</cffunction>
<cffunction name="_saveFoo" hint="saves Foo, and its children" access="public" returntype="void" output="false">
<cfargument name="foo" hint="The foo" type="Foo" required="Yes">
<cfset var children = arguments.foo.getFooChildrenArray() />
<cfset var child = 0 />
<cfset getTransfer().save(arguments.foo) />
<cfloop array="#children#" index="child">
<cfset getTransfer().save(child) />
</cfloop>
</cffunction>
So
the above code would execute the method _saveFoo, wrapped out in our
nesting-safe Transaction. Pretty cool, but a bit cludgy as we have to
implement a second method.
Aspect Oriented Programming
I think we can do better, and in fact we can, using the second method.
transaction.advise(component, function)
For those of you who are familiar with Aspect Oriented Programming (AOP), this method takes the component, and the function itself, and wraps a nesting-safe Transaction advise around the function that has been passed in.
If that was a little high level, to give an example, I could change the FooService to be:
<cffunction name="init" hint="saves Foo, and its children" access="public" returntype="void" output="false">
<cfargument name="transaction" hint="The transaction object"type="transfer.com.sql.transaction.Transaction" required="Yes">
<cfset arguments.transaction.advise(this, saveFoo) />
</cffunction>
<cffunction name="saveFoo" hint="saves Foo, and its children" access="public" returntype="void" output="false">
<cfargument name="foo" hint="The foo" type="Foo" required="Yes">
<cfset var children = arguments.foo.getFooChildrenArray() />
<cfset var child = 0 />
<cfset getTransfer().save(arguments.foo) />
<cfloop array="#children#" index="child">
<cfset getTransfer().save(child) />
</cfloop>
</cffunction>
So
what happens at init() time, the original saveFoo() is wrapped up in a
nesting-safe transaction, and you don't have to write much extra code,
except to remove the old <cftransaction> tags, and tell the
Transaction object what methods to advise. Much better than the first
way, and it works on both private and public methods.
So the
above, is very nice, however, what happens if we have a lot of
functions we want to advise()? Well, the third way of using the
Transaction Object is handy for that!
transaction.advise(component, regex, [debug])
With
this approach, we can apply the Transaction advise to all methods, both
public and private whose name matches the given regular expression.
For
example, if we want to apply Transaction advice to every method that
starts with 'save' in our FooService, we would just need to have our
FooService written like:
<cffunction name="init" hint="saves Foo, and its children" access="public" returntype="void" output="false">
<cfargument name="transaction" hint="The transaction object"type="transfer.com.sql.transaction.Transaction" required="Yes">
<cfset arguments.transaction.advise(this, "^save") />
</cffunction>
<cffunction name="saveFoo" hint="saves Foo, and its children" access="public" returntype="void" output="false">
<cfargument name="foo" hint="The foo" type="Foo" required="Yes">
<cfset var children = arguments.foo.getFooChildrenArray() />
<cfset var child = 0 />
<cfset getTransfer().save(arguments.foo) />
<cfloop array="#children#" index="child">
<cfset getTransfer().save(child) />
</cfloop>
</cffunction>
And
we are done. If we ever add a method that has 'save' at the beginning
of its name, such as the saveFooParent() from before, then it will
automatically be wrapped up in a Transaction, even if the method is
private.
If the 'debug' argument is set to true, the Transaction
object will <cftrace> all the methods that are advised, so you
can see if your method is being wrapped up in a Transaction or not.
I don't often say this (in fact, I avoid it whenever possible), but this is
the official best practice for doing Transactions with Transfer ORM. The usage of the optional 'useTransaction' argument is now officially deprecated.
Now I'll get back to this documentation stuff.... this is the last feature! I swear!
Big thanks to Jared Rypka-Hauer for letting me bounce this idea off him as well!
From the end of February 2008,
Transfer will be become Professional Open Source Software. This means that
Compound Theory will start selling services that are based around Transfer, while still retaining its free, Open Source licence.
The idea behind all this is such that I can do
more for
Transfer than I am currently doing, both in terms of being able to
write code, as well giving me more freedom to present, and travel to
conferences to physically talk to people.
Not only will Compound Theory be providing a series of services
based on Transfer ORM, it will also be offering project based
consulting, training, and mentoring.
To that effect, if you are interested in Compound Theory's services, please feel free to
contact me directly.
On the Transfer front, understandably, things are moving forward pretty fast.
Right now, I'm taking
Expressions of Interest in the following areas:
-
Transfer Support
A subscription and/or per incident model for support as pertaining to Transfer installation, use and/or any overall issues.
-
Transfer Training
A
Transfer curriculum that will come in a series of short courses, as
well as single/multiple day training programs, covering both on-line
training, and in-person training.
-
Transfer Developer Certification
Much
like ColdFusion Developer Certification,a Developer certification give
you a listing on the new Transfer site as certified, as well as a
spiffy certificate.
-
Transfer Training Certification
Again,
much like ColdFusion Training Certification, this would give you a
listing on the Certified Trainers page on the Transfer ORM site, as
well as access to the training materials.
If you are interested in any of the above programs and/or services, please send me an email either on my
contact form, or via the
Transfer Google Group. Once we have launched these programs, I will be in contact to let you know of the specific details.
If anyone wants any more information on Professional Open Source Software, '
The Beekeeper' article by
Pentaho Commercial Open Source Software is one the best descriptions of POSS I have ever read.
So with all that, what are the specific plans for Transfer in the near future? Lots of things!
First of all, the SVN version of Transfer is currently in
feature freeze. I'm only fixing bug fixes at this stage, to prepare for a Release Candidate of 0.7.
This release candidate will coincide with the new Wiki that will power
and house the extended documentation, both for the new features of
Transfer, and expansion on current feature sets. This Wiki is
currently under development, and you should be seeing something on it
soon.
The Wiki will be a new part of the new Transfer site that I have been
promising for a long time. The design for this has been done, and it
looks fantastic. This will consolidate things like bug tracking,
project and knowledge management into a single space, which will be
very useful in the long run.
Once that is all up and running, we're going to be moving as quick as we can towards a Transfer 1.0!
I have revised the road map, and pushed out several pieces to be implemented post 1.0:
Included in the 1.0 Roadmap
-
Binary Data Support
Currently Transfer doesn't support being able to insert or retrieve binary data from the database.
-
Programmatic setting of the data source details through the Configuration Bean
This will give a developer the ability to programmatically set the
details of the data source, which is particularly useful when the
datasource name is dynamically generated.
-
Generated methods all have hints on them, for TransferDoc and/or if displaying the object meta data in any way
Currently some, but not all methods that are generated have a populated
hint="" on their <cffunction> definitions. This will be expanded
to include all <cffunctions>, to enhance generated documentation
-
Ensure hashing is unique for all changes to the transfer.xml, by
ensuring that changes to any parents, flow through to generating new
definitions for children.
There are currently a few rare occurrences where Transfer .transfer
files won't be regenerated when a change is made to the XML file.
These rarities will be fixed.
-
Finalise CLOB support across all DBs.
CLOB support has a few issues across some DBs, specifically Oracle. This will be resolved so that it is no longer an issue.
-
Cascading insert, updates, delete and saves
This is what it says it is.
-
Programmatic reloading of the XML file.
The ability to be able to programatically reload the XML configuration file from within Transfer, to aid in development.
Pushed out to post 1.0 release
-
Soft Deletes
I have some more flexible ideas on how
to go about this, that will provide soft delete functionality, as well
as some other far more extensible possibilities.
-
TQL on conditions
This will allow you to use TQL on <condition> declarations,
however, I'm not sure if <conditions> are necessary any more, as
it may be superseded by some other ideas I have
-
Transaction based caching that discards objects inside a transaction if the transaction fails.
The way I wanted to implement this was not actually possible with
ColdFusion, but I have some other ideas on how to facilitate at least
something similar
From here there is also a strong plan to be releasing on a regular and
frequent schedule. I am fully aware of the fact that it's almost been
a year since the last release, but for those of you who haven't
followed the SVN version of Transfer, will be pleasantly surprised at
how much we've managed to fit into this new release.
The goal is to have the 1.0 of Transfer at least at Release Candidate stage by
cf.Objective() this year. Even though it's going to be a lot of hard work, I think that is is truly possible.
All in all, this is a pretty exciting time for Transfer, so I want to
thank all those who have participated up until now, we've developed a
great community, and 2008 is looking to be a very fun, and exciting
year for all involved.
Paul Marcotte at
Fancy Bread has written a
tutorial on the Transfer event model, based on the User password encryption example I gave during my
Advanced Transfer ORM Techniques talk.
He
does a really good job of breaking down the code example, and provides
some extra information above and beyond what I had originally provided
in my presentation, including some tips on integrating with
ColdSpring.
Yesterday I got notified that I will be speaking at cf.Objective()
next year! I am very excited about coming over again and speaking.
Last year was an amazing experience, and it will be great to catch up
with all the people who I only ever get to see face to face once a year.
I'm going to be doing two presentations on Transfer:
'Introduction to Building Applications with Transfer ORM' - A reworking
of my original 'intro' talk, that is going to take a very code centric
walkthrough of setting up and using Transfer ORM.
Transfer ORM Caching Mechanics' - Where we will look at some overall
caching concepts, and have a strong technical discussion on how the
caching in Transfer works, as well as all the configuration options,
and cache manipulation methods that are available.
I have to say, the speaker line up this year looks absolutely amazing.
I'm actually seriously hoping that my speaking schedule doesn't get in
the way of me getting to all the sessions I want to get to!
Big Kudos to Jared, Sean, and everyone else who's been working on the cf.Objective() conference, you guys are doing an incredible job.
We have a reasonably large application that we have been building for the past 2 years or so, with
MachII,
ColdSpring and
Transfer, and we moved over to ColdFusion 8, pretty much as soon as the Beta came out, and have been developing with it ever since.
To give you an idea of the size of the project, some metrics for you, we have 1064 CFCs, 329 .cfm pages, 161 configured Transfer Objects, 40 MachII
frameworks instances (we started this before modules), and 201 Tables.
This actually results in about 22,000 .class files being generated by
ColdFusion behind the scenes.
What we began to experience is
extremely long start up times when the server was first started up. For
example with no class files saved on the server, we have a start time
of around 20 minutes before the application was responsive, and if
multiple people hit the site at once, we were looking at around 45
minutes before the application would perform usably.
With class
files saved, this dropped down to 9 minutes, but we still ran into huge
difficulties with multiple users hitting the site at once. On top of
that, whenever we had to upload a change, clearing the template cache
so the change would propagate brought the server to it's knees, so we
were forced to restart CF with every deployment of code.
So
after much haranguing, and talking to a whole slew of people, both
Adobe, and non Adobe (you all know who you are, and thank you
very
much for the time and effort you all put into helping out on this), it
eventually got narrowed down to a bottleneck in Java 6 as
described here (as also reported by
Sean Corfield )
So
for those of you not so familiar with Java, what does all that actually
mean? Well, let's look at what ColdFusion does behind the scenes when
you run a .cfm or .cfc page:
- Checks to see if it has the Java Classes already in memory to do what you have requested - if not,
- CF Reads in the file you are executing
- Parses the CFML
- Converts the parsed CFML data structure, and converts it into Java code
- Compiled that Java code down to actual Java .class files
- Loads the resulting .class file into memory
- Executes the Java code contained in the .class file as necessary.
So
probably nothing too revolutionary in there in terms of our
understanding of the ColdFusion process, however, there is a big bottle
neck in Java 6 where the loading into memory Java classes is
really really really slow (maybe I need another
really there), so step 1.5 on the above processes takes a very long time to get through - and the problem only gets
worse
when there are large numbers of very small classes - which in almost
any ColdFusion application, and ours in particular, there are.
There are several places in which this issue can cause serious problems -
- At System Start Up
As discussed previously.
- Development.
Obviously during development, files tend to change quite regularly.
During development of our application, it would not be strange for me
to be spending several minutes, to the tens of minutes waiting for a small change in either a .cfm or .cfc to come through. This simply slows down the amount of work that you are able to do in any given time frame.
- At Run Time
Since ColdFusion has a finite limit on the number of cached templates
(which are just the .class files mentioned earlier stored in memory),
it is quite likely that at some point during an application life cycle,
part of what is stored in the template cache will get purged so as to
make room for other Java classes that have become active. We hit the
same issue as we hit on step 1.5 as above, as the ColdFusion server
slows down as it pulls in the required Java classes into memory. This
can result in random slowdowns in the application, which are hard to
reproduce.
This becomes a larger issue on shared host systems, in which a trusted
template cache is impossible, and it is quite likely the ColdFusion
server is constantly moving the generated Java code in and out of it's
template cache, as multiple systems require different ColdFusion code
to be executed.
It wasn't until we had one of those head-slap moments when a co-worker
turned around to me and said 'well.. why don't we try Java 1.5?'?
Once installed, suddenly everything started working like we wanted it to, performance wise.
Since everyone loves a pretty graph, here are some metrics on server
start up, taking a Selenium script through a series of steps through
one part of our application, so you can see the considerable difference
between the two Java Versions
Not only has this made our production systems run incredibly fast, it
also means that we are able to upload code to the production server,
clear the template cache, clear the application cache, and we are good
to go, there is no need to restart ColdFusion for changes.
This also means I can develop without having to take a coffee break in
between code changes, which has upped my productivity as well, in fact,
I can now develop happily in Machii with the config mode set to
constantly reload, and performance is no problem at all.
As far as I am aware, this issue does not exist in the development
snapshots of Java 7, and apparently a fix is in the pipeline for 1.6,
but Sun hasn't been forthcoming about the date.
Until that time, I would suggest moving your ColdFusion 8 servers over to Java 1.5, and enjoy the speed improvements!!!
Jared just did a blog post over at
Alagad on a feature request for named transactions. I have to chime in here and say that I
really, really, really, want this feature!
The current restriction of having transactions only within a <cftransaction></cftransaction> block is incredibly limiting from a OO perspective.
Something that I had on the radar for
Transfer
was to be able to control the Transactions at a database level with
your code, as well as automatically clearing from cache the relevant
objects if something went wrong.
Essentially it would go something like this:
transaction = getTransfer().getTransaction();
try
{
transaction.begin();
employee = getTransfer().get("employee", 1);
employee.setUsername(form.username);
getTransfer().save(employee);
manager = getTransfer().get("employee", 4);
manager.addEmployee(employee);
getTransfer().save(manager);
transaction.commit();
}
catch(Any exc)
{
transaction.rollback();
}
With
this code, if we got to the 'transaction.rollback()', not only would it
roll the DB data back, but it would also intelligently discard the
'employee' and the 'manager' objects from cache - thus ensuring that
there was no dirty data.
Currently, with ColdFusion transactions
structured the way they are, it's actually impossible for me to do
this, as there is no <cftransaction> block, and I have no hook to pick up when something goes wrong.
With a named Transaction block, I could internally handle how transactions where managed inside my Transaction.cfc, and and I could pick up the rollback() or commit() call as appropriate.
This would be an incredibly handy thing, so count me on a big
+1 for this feature!
I recently did a Connect presentation to the
IECFUG group, and it went really well!
I recently moved to a new ISP
at a higher speed, so this presentation is missing out on all the audio
clipping and dropouts of its predecessors, which I am really happy
about.
If you haven't managed to catch this presentation, and want to have a listen, the recording can be found
here.
On the 28th of November, 12pm UK time, I will be presenting via
Connect my 'Advanced' Transfer ORM talk, covering some of the
functionality of Transfer that is above and beyond the usual CRUD stuff.
This
includes things like the caching layer, the observable events,
Decorators, and Transfer Query Language, which aren't covered in the
introductory talk that I often do.
More details on the talk can be found
at the cfframeworks site.
I hope you all enjoy it,as I have yet to do this one online yet! So this will be the first! Should be good!!!
First of all, you may be wondering 'what on earth is a JavaProxy?', well,
to answer that question, it is the Java class that does all the work behind the
scenes in ColdFusion to allow you to be able to write all that Java code in-line
in your ColdFusion CFCs and CFM pages by taking the Coldfusion invocations you
have implemented, and passed them to the native Java objects that you want to
use.
To further your understanding, if you are at all interested, you can also read
up on the
proxy
design pattern here.
Now, what some people may or may not realise, is that inside
JavaLoader,
I create an instance of the coldfusion.runtime.java.JavaProxy class, so it
becomes really easy for developers to create and use instances of Java objects
that are loaded from external .jar files within their applications. I have
a good blog post on doing this
here.
Now just the other day, I became aware of a new setting in ColdFusion 8 entitled
'Disable Access to internal ColdFusion Java components', that really threw me
for a bend.
For people who run shared hosts, they probably think of this as a g-d send, in
that it will disable access to coldfusion.runtime.ServiceFactory - and for that,
I totally understand, however, it completely locks down access to any Java
object that sits under the coldfusion.* package space.
What does this mean? It means that JavaLoader, no longer works, along with
any
other
project
that also
uses JavaLoader could quite potentially not work on some shared hosts providers
that have upgraded to ColdFusion 8!
Why did Adobe decided to do this? Not so sure! However, with the power that we have
in ColdFusion 8, we are able to implement our own JavaProxy, that should be able
to be seamlessly interchanged with the ColdFusion native JavaProxy!
(Disclaimer: This is the first run at this code, and it works in the given tests
I have tried on it. I will be running it against all the unit tests on
Transfer, and when they all work perfectly, and Transfer can run with this CF8
restriction in place, I will release the full code as part of a new version of
JavaLoader)
There are two things that allow us to do this -
-
Nothing stopping us from using Java Reflection to dynamically call methods
on a Java Class or Instance.
-
In CF8 we got 'onMissingMethod()' - so we have a hook into every method that
is fired on a CFC, regardless of whether or not it has been implemented.
For those of you who aren't that familiar with the term
reflection - it essentially means that we
are able to introspect a Java Class or Object, determine what methods and/or
properties it has, and dynamically call them at run-time. If you want to
read more, the Java site has a whole
tutorial
on reflection.
So, first of all, let's look at the few different ways you can instantiate and
use ColdFusion Java Objects, we'll use an
ArrayList
as an example, so that we know what we need to support in our own
JavaProxy.
I will use 'createObject' here, but it could also just as easily be
JavaLoader.create(
className) to create an instance of the JavaProxy.
-
array = createObject("java", "java.util.ArrayList").init();
array.add(obj);
- This would be the most common, and generally the 'best' way of
instantiating a Java Object in CF, as it calls the constructor straight
away, with the appropriate arguments, and is the closest you will get, in
style, to implementing a real constructor.
-
array = createObject("java", "java.util.ArrayList");
array.init();
array.add(obj);
- I've seen this sort of code before... to me, it seems a bit weird to split
out the constructor, but it works, and you always know that the object has
been instantiated.
-
array = createObject("java", "java.util.ArrayList");
array.add(obj);
- This is what I feel is the 'worst' way of using Java objects in CF as the
no argument default constructor is called implicitly, which means you really
have no control, and it can lead to all sorts of weirdness in your
application if you don't track what has been actually instantiated and what
hasn't.
-
Collections = createObject("java", "java.util.Collections");
sortedArray = Collections.sort(array);
- This case shows where static methods are called, in which case, there is
no constructor.
-
Color = createObject("java", "java.awt.Color");
black = Color.black;
- This is where we want to be able to retrieve a static property.
Okay, so we have our work cut out for us! But all this is very much possible!
So, the first decision to make, is that all of our internal methods on the
JavaProxy.cfc are going to start with an underscore. This is so that any
method that we write, doesn't interfere with the onMissingMethod's we want to be
able to pick up. Also as we need to implement a special 'init' method,
that isn't the constructor for the JavaProxy, but instead is a constructor for
the Java object the JavaProxy represents, so we will have a _init(
class)
method that instantiates the JavaProxy.
I'm not going to show all the code here, just the relevant parts, but don't
worry, you will be able to see it in the next version of JavaLoader.
So the _init method will do the following things -
-
Take a Java
Class
as an argument, and store it in state
-
Store some helpful Java Objects in some setters.
-
Set the Static
Fields
of the Class the JavaProxy represents to the this scope
-
Store all the
Method
objects of the Class in a struct of arrays for easy lookup (more on this
later).
First of all, we'll set the Static Fields to the
this scope. It is
actually very straight forward -
<cffunction name="_setStaticFields" hint="loops around all the fields andsets the static one to this scope" access="private" returntype="void"output="false">
<cfscript>
var fields = _getClass().getFields();
var counter = 1;
var len = ArrayLen(fields);
var field = 0;
for(; counter <= len; counter++)
{
field =fields[counter];
if(_getModifier().isStatic(field.getModifiers()))
{
this[field.getName()] = field.get(JavaCast("null", 0));
}
}
</cfscript>
</cffunction>
For reference
So what are we doing here? Well, we ask the Class for all of it's Fields, then
we look around them, and then interrogate into whether or not they are static.
Once we know they are static, we retrieve their value, using field.get(), and
set them to the same place in the
this
scope as they would have been in the Java Object.
We are able to use 'null' on the field.get() because the values are static, and
are not tied to any actual instance of the Class.
So, what would be nice now, is to be actually be able to instantiate an
object! So let's look at implementing our own 'init' method, so that we
can instantiate the Java Class that the JavaProxy represents.
<cffunction name="init" hint="create an instance of this object"access="public" returntype="any" output="false">
<cfscript>
var constructor = 0;
var instance = 0;
//make sure we only ever have one instance
if(_hasClassInstance())
{
return _getClassInstance();
}
constructor =_resolveMethodByParams("Constructor", _getClass().getConstructors(), arguments);
instance =constructor.newInstance(_buildArgumentArray(arguments));
_setClassInstance(instance);
return _getClassInstance();
</cfscript>
</cffunction>
For reference
-
_buildArgumentArray() simply takes the argument struct, and turns it into an
actual Java Array of the same objects.
So first off, we check to see if we already have created an instance - because
we only want one, otherwise weird stuff could happen. If we do, just give
back the Java instance we already have.
The next line, is a little bit more complicated, so we'll break it down.
The _getClass().getConstructors() returns an array of all the possible
Constructors
that are available for this given class.
The _resolveMethodByParams() method takes a array of Method/Constructor objects,
the arguments that have been passed through to the given method, in this case
'init', and find the best match that it can, and returns it. We'll go into
the details of that in a minute.
Once we have the right Constructor object, to get an instance of the Object that our JavaProxy represents, we call
'newInstance' on it, and pass in an array of the objects that make up the
arguments that the Constructor needs.
And Preso! We have an instance of our Class! We set it to the state of
the Class Instance, and return the newly created instance back out.
Wait! What? Return the new created instance? Why aren't we returning
this, that doesn't make sense? Well actually, if you think about it, it does.
We
really would prefer it if ColdFusion did all the heavy lifting when it
comes to the bridge between Java and ColdFusion, not only is it more
performant, but it also provides a greater deal of consistency across
the code base.
So we have code that is:
obj = JavaProxy.init();
obj
is actually an instance of the ColdFusion JavaProxy, and then there is
a much more seamless line between the new CFC JavaProxy, and the use of
the ColdFusion one, which is a very good thing.
That being said,
we need to provide support for all the different types of ways that
Java Objects can be used and created, so we have to also cater for the
other aspects as well.
So without further ado, let's actually fire off some methods! This is where onMissingMethod really comes into it's power!
<cffunction name="onMissingMethod" access="public" returntype="any"output="false" hint="wires the coldfusion invocation to the JavaObject">
<cfargument name="missingMethodName" type="string" required="true" />
<cfargument name="missingMethodArguments" type="struct" required="true" />
<cfscript>
var method = _findMethod(arguments.missingMethodName, arguments.missingMethodArguments);
if(_getModifier().isStatic(method.getModifiers()))
{
return method.invoke(JavaCast("null", 0), _buildArgumentArray(arguments.missingMethodArguments));
}
else
{
if(NOT _hasClassInstance())
{
//run the default constructor, just like in normal CF, if there is no instance
init();
}
return method.invoke(_getClassInstance(), _buildArgumentArray(arguments.missingMethodArguments));
}
</cfscript>
</cffunction>
Okay, so this is the code that actually takes the methods that are
called on the JavaProxy, and passes them to the Java instance or Class
as appropriate.
The _findMethod() method, returns the Method
that best matches the name of the method that was called, and the
arguments that it has. I will go into detail on that in just a second.
Once we have the correct Method, if it is static, we can then invoke it against 'null', and return it's value.
If
it isn't static, then we check to see if we have a instance of the Java
Class yet, if not, we create one using the default Constructor, which
is the same way that ColdFusion does it.
From here, we are able to invoke the method against the class instance, and return any results that we may get.
Now we can look at the logic that allows us to work out which method matches what in ColdFusion.
Our first step, is to look at the _findMethod method, which is actually pretty simple:
<cffunction name="_findMethod" hint="finds the method thatclosest matches the signature" access="public" returntype="any"output="false">
<cfargument name="methodName" hint="the name of the method" type="string" required="Yes">
<cfargument name="methodArgs" hint="the arguments to look for" type="struct" required="Yes">
<cfscript>
var decision = 0;
if(StructKeyExists(_getMethodCollection(), arguments.methodName))
{
decision = StructFind(_getMethodCollection(), arguments.methodName);
//if there is only one option, try it, it's only going to throw a runtime exception if it doesn't work.
if(ArrayLen(decision) == 1)
{
return decision[1];
}
else
{
return _resolveMethodByParams(arguments.methodName, decision, arguments.methodArgs);
}
}
throw("JavaProxy.MethodNotFoundException", "Could not find thedesignated method", "Could not find the method '#arguments.methodName#'in the class #_getClass().getName()#");
</cfscript>
</cffunction>
The first thing to know is, that _getMethodCollection() returns a
struct of arrays that was set up in our _init(), the key of which is
the name of the methods found in the class. The arrays contained in
the struct have all the Methods that have that name, as there may be
more than one.
So, the first thing we do, is check to see if the
name of the method we need is in the collection of methods we have, if
it is we go and grab the array of methods this invocation could
possibly be.
You will notice that I have written code that
states 'if you only have one option for the method, just return that'.
You may be wondering why, as the parameters of that method may not
match what has been passed in. Well, if that is the case, we will get
a runtime error, which is the same as what we would get otherwise, so
there is not a huge difference here to just say 'let's give this a
shot, if it doesn't work, no big deal', and we save the performance hit
of comparing parameters.
If there are more than one option
available, then we have to start comparing parameters, and this is
where the _resolveMethodByParams() method that we saw earlier does it's
hard work.
<cffunction name="_resolveMethodByParams"hint="resolves the method to use by the parameters provided"access="private" returntype="any" output="false">
<cfargument name="methodName" hint="the name of the method" type="string" required="Yes">
<cfargument name="decision" hint="the array of methods to decide from" type="array" required="Yes">
<cfargument name="methodArgs" hint="the arguments to look for" type="struct" required="Yes">
<cfscript>
var decisionLen = ArrayLen(arguments.decision);
var method = 0;
var counter = 1;
var argLen = ArrayLen(arguments.methodArgs);
var paremeters = 0;
var paramLen = 0;
var pCounter = 0;
var param = 0;
var class = 0;
var found = true;
for(; counter <= decisionLen; counter++)
{
method = arguments.decision[counter];
parameters = method.getParameterTypes();
paramLen = ArrayLen(parameters);
found = true;
if(argLen eq paramLen)
{
for(pCounter = 1; pCounter <= paramLen AND found; pCounter++)
{
param = parameters[pCounter];
class = _getClassMethod().invoke(arguments.methodArgs[pCounter], JavaCast("null", 0));
if(param.isAssignableFrom(class))
{
found = true;
}
else if(param.isPrimitive()) //if it's a primitive, it can be mapped to object primtive classes
{
if(param.getName() eq "boolean" AND class.getName() eq "java.lang.Boolean")
{
found = true;
}
else if(param.getName() eq "int" AND class.getName() eq "java.lang.Integer")
{
found = true;
}
...
else
{
throw("Ack", "Cannot match this primitive type", "'#param.getName()#' is just not matching");
}
}
else
{
found = false;
}
}
if(found)
{
return method;
}
}
}
throw("JavaProxy.MethodNotFoundException", "Could not find thedesignated method", "Could not find the method '#arguments.methodName#'in the class #_getClass().getName()#");
</cfscript>
</cffunction>
Woah! That's a lot of crazy code... well, it's not too bad once you break it down.
What we are doing is looping around all the possible methods we have available in the
decision array, and trying to see if they match the parameters that we have in our argument struct.
First test says, 'if the number of parameters is different, well, we can't invoke this method', and simply passes it by.
From
there, we need to loop around each of the parameters, and see if it can
work with the class that the corresponding argument that matches it's
place.
You're probably looking at the line that reads '_getClassMethod().invoke(arguments.methodArgs[pCounter], JavaCast("null", 0));' and thinking... what on earth does that do? Well, it allows us to get to the Class object of that argument.
This
is very similar to doing a obj.getClass(), however, I can't do that in
all instances. If an argument is a CFC, then it will try and resolve
the method 'getClasss' against the CFC, and most likely throw me an
error. So I have to use reflection!
the _getClassMethod()
(this was setup in our _init()) is the actually Method class that
represents the 'getClass()' method aforementioned, what I then do is
invoke it on the required argument, and this gives me back the Class
Object I need for comparison.
Now, a Class object has
a great method called 'isAssignableFrom', basically, this says 'If the
object is a the same as this Class, or a subclass, or implements this
interface, return true'. This means that if the argument in the method
that have been invoked on the CF side isAssignable to the parameter
that is required for this method, then this method can be invoked
successfully.
The other thing we need to do is map primitive
parameters, such as int, char, boolean, etc back to their Object
representations. The nice thing is that Java will map this back and
forth at runtime for us, so as long as the values match up, we're good
to go.
Once the parameters have all been resolved, we can return the method we have found that works, otherwise, we throw an exception.
That
pretty much covers the JavaProxy.CFC. As I said before, you will be
able to see the code in action once I release the new version of
JavaLoader, but feel free to ask any questions you may have, I will be
happy to answer them.
Paul Marcotte has written an interesting series on how he is building his own administration scaffolding using his own MVC framework,
Dispatcher, and
Transfer.
In his latest post,
he adds Transfer into the mix and shows some good examples of what can
be done when retrieving the Meta Data from Transfer about the Objects
that are defined within it.
Nick Tong bullied me into doing another online presentation of my Developing Applications with
Transfer talk ;o) So it's all lined up and ready to go for the 30th of August.
Full details can be seen here:
http://www.cfframeworks.com/blog/index.cfm/2007/8/16/Workshop-Mark-Mandel---Developing-Applications-with-Transfer-ORM
This presentation will show off the branding for Transfer, which those of your not at
cf.Objective probably haven't had a chance to see (and also since I haven't done the new Transfer site I've been promising for millennium)
Depending on how time goes, you may even get sneak peeks at the new
Composite Key functionality I'm about three quarters of the way through.
It
should also be worth noting that the following month, I will be doing
my Advanced Transfer ORM Techniques presentation, which has not been
seen outside of cf.Objective before!
Nick Tong over at
Succor has posted a
Fusebox lexicon for using TQL ! Pretty neat stuff!
If you like
Fusebox, and you like
Transfer, I suggest having a look.
I have to say, it's really cool watching all these framework work together... ;o)
Yes, it's true, I've finally succumbed, and put ads on my site, and
switched out the wishlist for a PayPal donate button. I have finally
turned to the dark side.
Do not fear! There are reasons for this!
First of all, the Amazon wish list, didn't really work out. I'm sure
people thought 'I'll buy Mark something from the wishlist, he'll like
that', and then quickly realised it can be up to $30 to send stuff to
Australia, and that whole idea quickly went out the window.
Second of all, I really want to get out to more conferences overseas.
Unfortunately, while living in Australia is wonderful, we are about as
far away from anyone as can possibly be. This means that travelling
can be
really expensive. So this is to say that any revenue
and/or donations that I receive from the ads, or from PayPal, will go directly to funding conference travel costs, and
also to general open source development costs.
So if you do like the work that I've provided for you guys, please feel
free to click the PayPal donate button, its always appreciated, and
allows me to come out to more conferences, and put more resources at
your disposal.
Last week we had Robin Hilliard of
Rocketboots into the office to help us get the biggest bang for our buck performance wise with the
Transfer /
MachII /
ColdSpring application, and I have to say it was a great session all around.
Apart from imparting upon us a great many ideas for aspects of our
application we could cache, and various other pearls of wisdom, we
turned on Report Execution Times, and managed to find several key
places in Transfer that were sometimes called over 500 times in one
request (we do a fair amount of data movement per request).
For one thing, in all honesty, I had completely forgotten about Report
Execution Times. I had turned it off when it was making my CFC heavy
applications go into a slow paced crawl, and had quite literally left
it for dust.
However, after turning it on, and just running it over just a few
requests, the key areas of Transfer that would provide significant
performance increases became very apparent very quickly.
This is where all those small, finicky, performance 'tricks' come to the fore very quick -
- using else/if statements rather than case statements
- replacing the use of iterators with for() loops that use a counting numeric index
- playing with different Java Collections for different operations - i.e. ArrayList vs LinkedList
- Assigning method results to variables before large looping operations, rather than evaluating them every time.
- ..and other such things
It should be worth noting, that by doing some of these things, the code
ended up looking rather ugly, but with testing, performs faster than
before. So this is not to say that I went through the entire Transfer
codebase and switched out everything I could find to being the most
efficient I could possibly, in fact that couldn't be further from the
truth. There are many places in Transfer that use case statements,
even knowing that else/if statements are faster - simply because I find
case statements are more readable, and they are not in places of the
system that are called in numerous succession. By the same token, I
make extensive use of Java iterators to loop over my collections in
Transfer, as they provide a high degree of abstraction away from what
sort of Collection is being used behind the scenes, but those places
are now limited. However, by specifically pin pointing aspects of the
system that are critical to the performance of the framework, the
necessary tweaks to the framework could be discovered and acted upon.
So say thanks for Robin for the new performance improvements for Transfer that can now be found in
SVN.
Brian Rinaldi just
finished up
a great tutorial on how to handle Object Composition, going from
creating CFCs from scratch, through to modelling the entire thing with
ColdSpring and
Transfer.
He goes into some great detail on how to use his
Illudium PU-36 code generator to generate the Transfer templates to make life a lot easier for the outset.
Definitely an interesting read.
I just checked out the
mailing list for
Transfer, and we're currently at 130 people!
Wow!
When I left for
cf.Objective(),
we were at 94, and I was hoping we would soon be at 100... I had a peek
today, and we're actually at 130! That's an increase of 36 people since
cf.Objective() Started!
That's 1.6 people added to the list, per day, since the day cf.Objective() started.
I
can only assume that this as a sign that my presentations were well
received, and have generated some interest in Transfer, which I am very
glad to hear.
(By the way, if anyone has any feedback on those presentations, please
feel free to let me know, I actually get very little in the way of
feedback, so I'm actually all ears)
I'm currently working on the getting the Composite key functionality
off the ground, so things are still moving forward with Transfer, so
expect some news on that in the near future.
Now that things have finally settled down getting back to Australia
(and I finally have a laptop that I can use again), I can put some of
my thoughts down on (virtual) paper on the recent
cf.Objective() conference.
First
of all, let me just say that this conference was the single best
ColdFusion learning experience I have ever had. I don't think I have
learned as much, in such a short period at any other conference,
mailing list, chat room, or
CFUG meeting.
I
have to seriously take my hat off to Jared, Steven and the rest of the
crew that put together this event - the facilities were excellent, the
speakers were phenomenal, and the food was fantastic (those chocolate
cakes!).
When I wasn't busy presenting on
Transfer,
I actually got to run around and sit in on a whole lot of
presentations. Again, these presentations were a level above and
beyond any I have seen elsewhere, so much so, that I can't pick out
specific ones in which I would say 'these are were the highlights for
me', otherwise, I'd just be listing 90% of the sessions I went to.
For
those of you who are still waiting on my presentation slides, please be
patient. I only just got ColdFusion back up and running on my
resurrected laptop, so I've got a little ways to go. Thankfully all
the slides where backed up, and I just need to do some work on the
example application before I can make it available for download.
Coming
from Australia, and actually physically meeting so many of the people
that I talk to online, day in, day out, was an absolute pleasure. A
few of you I had met before, but most of you I have only ever talked to
via text. It is always amazing how much more you get out of simply
physically sitting in the same room as someone and being able to chat
about things, than you can via something like
IRC.
While
the presentations were incredible, I think I probably took more away
from talking to people about ColdFusion at cf.Objective(). The
environment and the people that were around really lent itself to
creating some really interesting discussions about ColdFusion and
various other software development topics. I often woke up at 5:30am
and simply couldn't get to sleep again because I had to get some
thoughts down on paper, or I had to actually start writing some code.
The only complaint I had about the conference was the
complete lack of sleep
I got! I landed in Minneapolis in time for some dinner, having had
about 3 hours sleep, and then promptly got dragged into conversing
about ColdFusion until 2am in the morning. I think I actually worked
out, in over 72 hours of being awake, I had only managed around 11
hours sleep. Of course it was totally not by fault that I was forced
to stay up late drinking and talking geeky things. ;D
Honestly,
however, regardless of what country you reside in, if you are seriously
looking to expand you knowledge of enterprise development, be it
beginner, or advances, cf.Objective() is
the best learning experience you could have. Funds non-withstanding, I will definitely be there next year.
This may seem a little behind the times, but between my HD crashing at
conference, and trying to physically meet as many people as possible
(and have a few drinks too), I didn't blog that at
cf.Objective() I released the new logo and branding for Transfer!
This was designed by
my girlfriend, who, as you can see, can definitely do a far better job than my usual programmer artwork.
As well as this, there is an upcoming
www.transfer-orm.com
project site for Transfer. Currently this just points back to the
compoundtheory project page, but expect to see a Transfer specific
page, with a wiki, coldfusion based trac and all sorts of other
interesting things.
I have yet to write up a full blog post on cf.Objective(), as I only
got back a couple of days ago, and have still been rebuilding my
laptop, so expect that, and presentation slides and example code etc to
come soon!
Phew!
over 24 hours of flying, travelling, waiting for bags, standing queues,
taking my shoes off, putting my shoes on, walking through scanners,
putting bags on conveyor belts, taking bags off conveyor belts.... I'm
finally here.
I have to say, the flights weren't all that bad,
Qantas's new 'on demand' in flight system was pretty good in that I
could pick and choose what TV and movies I wanted to watch for 14 hours.
If you need to reach me, I added myself to the
twitter back channel for
cf.Objective(), so if you add me as a friend, can chat that way. I'll most likely be on IM
most of the time I am here, and worst comes to worst, call the hotel
and ask for me by name - but please no calls before 11am, I'm currently
running on about 3 hour sleep.
Other than that, I should be pretty easy to spot around the place, look for the guy that looks like
this, with a dragon tattoo around his left bicep.
That being said, if anyone wants to catch up during the day on Thursday, please do get in contact.
See you all at the conference!
Yesterday morning (well, it was my morning, most other people's
afternoon), I did a short, 20 minute preview of one of the talks I am
doing at
cf.Objective().
If you managed to miss it, and want to have a listen, or are looking for a real quick overview of
Transfer, you can see the
recording here.
I have about three seconds to write this - but I'm doing a preview of my Developing Applications with Transfer ORM presentation for cf.Objective() at 3pm EST, on Friday the 27th of April (my tomorrow).
Please show up if you are interested, as I'm getting up at 5am to come talk to you lot ;o)
More details can be found at the ColdFusion Meetup Group .
Toby Tremayne has (finally? ;o) ) released an
Alpha version of his Flex to Transfer bridge Bender.
Bender looks to be a very interesting project, in that it generates the AS for you that mirrors the generated TransferObjects that come our of
Transfer.
It also handles passing data back and forth between Transfer and
Flex, and back again, without you having to do any of the translation between.
The
neat thing about Bender as well, is that you can set up mappings, such
that when you call Bender.save() on the AS side, it can either call
Transfer directly, or, you can send the save() request to a completely
different mapped CFC and/or method! This gives you complete
flexibility over your architecture.
I'm really liking the work that Toby is doing on Bender, and I think it's going to be a real asset to the Transfer library.
After a good Release Candidate phase,
Transfer 0.6.3 is ready for release.
There
is nothing huge to report in the change from RC2 to Final, except for a
few small code cleanups and some more documentation, and that is about
it.
For the big spiel about what is new in 0.6.3, check out the
RC1 release post.
Now it's just a case of seeing what I can manage to fit in before
cf.Objective(), which is in two weeks!
Enjoy!
Toby Tremayne has started doing some work on developing a bridge between
Flex and
Transfer
called 'Bender', which is meant to automate the process of translating
TransferObjects into Actionscript Value Objects and back again within a
Flex Application.
We are yet to see any code samples yet, but he tells me that progress
on it is running along nicely, and he already has a series of blog
posts on the progress he has made already.
I'm really looking forward to seeing the product in action, I think it is going to be really interesting! Have a read of it
here.
This is something that
Nick Tong, of
cfframeworks.com fame wrote a while ago, but I totally forgot to blog.
Nick wrote up a great little tutorial on how to set up Transfer when using
Fusebox with Fusebox Lexicons.
You can have a look at the tutorial
here.
Another release candidate for
Transfer.
This one updates some documentation, and implements a workaround for the CFMX
memory bug with URLClassLoaders described
here. Other than
that, nothing that exciting to report.
Mind you, the new tBlog example application has been updated to instead use
TQL
to do it's gateway queries, rather than using regular ol' SQL, and a
Decorator
example is also shown, so it may be worth re-downloading it if you already have it, and take a peek.
Since the
last
release candidate didn't show up any bugs, assuming I don't hear anything
about this one, v0.6.3 final will be ready to go in a week or so. Which is
really great, as no one has discovered any glaring holes in TQL yet.
After that, it's time to start developing Composite Key support. Cool!
This is a bug in ColdFusion that can cause memory leaks when using a
java.net.URLClassLoader
to load external jar files. Thus, this can cause memory leaks in
JavaLoader,
Transfer
and any other system that uses this technology. However there is a
workaround for the issue.
To explain the problem, first we need to look at some key issues with a
URLClassLoader. URLClassLoaders are notorious for causing memory leaks,
because, for them to be garbage collected, all instances to themselves
and the classes that they have created
need to be garbage collectible.
This means that if you access a class from a URLClassLoader and hold it
somewhere in memory, then the URLClassLoader can
never be garbage collected.
This is exactly what happens with ColdFusion.
When ColdFusion does some introspection and resolution of ColdFusion code
against a Java object, somewhere, deep inside its hidden internals, it keeps a
strong reference to the Class object that refers to that Java object. This
means that when the JVM comes along to garbage collect the instance of the
URLClassLoader, it can't do it, because ColdFusion has a reference to a class
that it loaded somewhere inside.
So, the memory leak only ever actually happens when an instance of a
URLClassLoader is no longer available to ColdFusion, as it is never then garbage
collected by the JVM.
How does this translate to using JavaLoader? Well, a perfect example of this is
where you put an instance of JavaLoader in the application scope, because
generally it is used as a singleton. JavaLoader (and anything that
subsequently uses JavaLoader) has an instance of a URLClassLoader inside
it. However, when the application scope times out, the JavaLoader CFC may
well be garbage collected, but the URLClassLoader isn't, which can cause a
memory leak.
To note however, in production systems the leak is minimised in situations like
this, as it is often very rare that the application scope will ever time out.
So what is the workaround for this issue? To note, I have been pushing at Adobe
to get a hotfix out for CFMX to resolve this issue, but we can definitely still
use this technique now, without having to worry about memory leaks.
Essentially, the memory leak only happens when the URLClassLoader is no longer
available to CF, i.e. an application scope times out, or something similar - so
we just need to make sure that it never, ever, times out. How can we do
that? why, put it in the
Server scope of
course!
Since variables in the Server scope never time out, we don't need to worry about
the URLClassLoader (or JavaLoader) being lost and then recreated, as it always
exists. As long as you put it in the Server scope under a key no one will
ever need to utilise (I like a hard coded UUID myself)! Hence we beat the
memory leak monster!
I have just committed a fix for Transfer that automatically puts the JavaLoader
it uses into the Server scope, so even if your TransferFactory times out, the
JavaLoader never will, which means there is no leak, and the RC2 for 0.6.3 will
have this as well.
Hopefully Adobe will put out a hotfix for this issue, but until then, put your
JavaLoaders in the Server scope.
I figured it was about time I made some comments on
webDU now that the dust has
settled (and I'm finally over the gastro I've had all week, including at the
conference).
Overall, I have to say I have an absolute blast, and it was
brilliant to put some real faces to people who were just names on a mail list,
IM or irc chat room.
There is a huge amount of kudos that needs to go to Geoff, Julie and the rest of
the
Daemon team for
putting on webDU, it was well organised, professional, and not to mention a
whole lot of fun.
The only criticism on the conference was that with the Flex heavy presentations,
there seemed to be a fair amount of overlap in terms of content covered. I
went to a few Flex presentations, and many of them covered that very beginning
level of introduction into Flex. While I am not a Flex user by any means,
I like to keep my eye on the Flex space, and was often looking for something
more than just another 'here is how to say 'Hello World' in Flex', especially
when the title of the presentation lends itself to make you believe it is
something more.
Otherwise, the presentations where very good, and on the whole insightful. My favourites included the
Keynote:
Flash Bang, Apollo,
FarCry 4.0: The Application Framework,
Seeding,
Developing and Growing an online Community, and
Using the IM gateway in ColdFusion .
I felt that my
Transfer presentation went very well, although I'm still not sure how I managed to get through all
39 slides in just over half an hour without someone telling me to speak slower ;o). I did have to laugh when I got the webDU booklet and my presentation slides took up something like 4 or 5 pages, compared to other presenters usual 2 or 3.
Speaking
to people after the session, the feedback was generally positive, and
people seemed to have walked away feeling like they had learnt
something. Mind you, if anyone has any extra feedback they wish to
give me on the presentation, I'm all ears, so fire away. I'm always
looking to do a better job.
Charlie Arehart and I did a
BOF: ColdFusion Componentry
session together, which, in all honestly, I didn't think anyone would
show up to, being 8am after the night of the banquet. But people did,
and we had a good chat about Object Oriented development, from a
variety of angles. I apologise if I wasn't making any sense, as I may
have possibly had my fair share of the Microsoft tab at the bar the
night before.
Speaking of which, the Banquet was great as well,
including the Kath and Kim impersonators. I think the most enjoyable
part was watching the American visitors try and work out who Kath and
Kim were. ;o)
The drinks at the bar after the banquet were also
lots of fun, but I am wondering where everyone went around 11pm. It
seemed like the place was packed, and then suddenly it was empty. Go
figure. I did get a definite giggle out of a Microsoft representative
asking me why I didn't program in .Net. All I know is that at around
2am they kicked us out, so I ended up going back to my room to crash.
The
definite highlight of the whole thing, I have to say, was the people.
It was such a good opportunity to run around and actually
physically
talk to a variety of people who worked with Adobe technologies.
(Especially to one guy who couldn't quite believe that I thought
manipulating a huge amount of financial data was really cool... I
didn't really care about the Flex part). To those of you I talked to,
and there were way to many to mention, was great to chat to you all,
and I'm glad to have (finally?) met you.
A brilliant conference,
and I hope that those of you who didn't come, will show up next year,
and those that I met this year, I will see again.
Wow. I have been pushing like crazy the next release of
Transfer finished before I fly out for
webDU, and I actually made it. I leave this afternoon for Sydney at 4:00pm, so I am cutting it fine.
This release has 2 great new features, as well as several critical bug
fixes, not to mention a greatly expanded documentation, including a new
Overview and FAQ.
Rather than try and explain these features all over again, I'm simply going to show off some of the Overview and FAQ:
From the Overview Section:
Transfer Query Language
There
is also a scripting language that allows you to perform database
queries based on the information and naming scheme that you set up in
your transfer configuration file called Transfer Query Language (TQL). TQL is very similar to SQL,
however since Transfer already knows about the relationships in your
system, you don't have to write as much code to perform complicated
queries against your database.
For example, if we
wanted to perform a query to list all Posts in my Blog System, with
their Author, and all the Categories they belonged to, ordered by the
post date we would write this in TQL:
<cfsavecontent variable="tql">
from
post.Post as Post
join system.Category
join user.User
order by
Post.dateTime desc
</cfsavecontent>
And then we would create a Transfer Query Object by passing the TQL to transfer.createQuery(), and then create the actual query we need by passing the Transfer Query Object to transfer.listByQuery(), as in the example below.
<cfscript>
query = transfer.createQuery(tql);
qPosts = transfer.listByQuery(query);
</cfscript>
Since Transfer already has the relationships between the Post, Category and User, it can intelligently create the SQL joins for you! Saving you even more time!
There's another Database Management Method you can use with TQL besides transfer.listByQuery(), and that's transfer.readByQuery(). readByQuery returns a Transfer Object, whereas listByQuery() returns a query. readByQuery() requires TQL
that will return only "one row" - if more than one row is returned, an
exception is thrown - and it also requires that you specify the class,
like all the otherreadBy* methods.
Basically, readByQuery()
is a way to create a Transfer Object when you don't want to use the
primary key, but specify some other condition that will return only one
record.
For more information on these Transfer methods, see
Using The Data Management Methods .
From the FAQ:
How do I inject dependencies into a TransferObject?
To inject dependencies into a TransferObject, the 'afterNew' event must be used.
To register an Observer for the afterNew event:
observer = createObject("component", "InjectorObserver").init();
getTransfer().addAfterNewObserver(observer);
The observer must have a method on it that looks like, which is run when the event is fired:
<cffunction name="actionAfterNewTransferEvent" hint="Do something on the new object" access="public" returntype="void" output="false">
<cfargument name="event" hint="" type="transfer.com.events.TransferEvent" required="Yes">
<!--- do stuff here --->
</cffunction>
The event is then fired *after* the TransferObject is init()'d and the configure() method is run.
So if you now want to inject something into a TransferObject, you will need to write a setter/getter on it - and you can do it something like this:
<cffunction name="actionAfterNewTransferEvent" hint="Do something on the new object" access="public" returntype="void" output="false">
<cfargument name="event" hint="" type="transfer.com.events.TransferEvent" required="Yes">
<cfif arguments.event.getTransferObject().getClassName() eq "user.User">
<cfset arguments.event.getTransferObject().setService(getService()) />
</cfif>
</cffunction>
A <cfcase> statement could be used instead of an <cfif> depending on your needs.
You could put this event directly on a Factory of some description if
you wanted to, or have it on a specific object whose only task is to
inject new
TransferObjects with dependencies, depending on your application design.
Full changelog.
A *HUGE* amount of thanks to
Nando, Jaime Metcher, and Aaron Roberson for all the hard work they put into the documentation. It was greatly appreciated.
You may have also noticed a skip in version numbers. It's okay, you're
not going totally crazy. That was just a decision that I made, to
evenly space out all the major feature releases between 0.6.1 and 0.7.
So now the release road map looks like:
0.6.3 : TQL
0.6.6 : Composite Keys
0.6.9 : Soft Deletes
0.7 : All the other small pieces I wanted to fit into 0.7
It also gives me some wiggle room in case I need/want to do a maintenance release between.
Now, I'm off to webDU, and if you are attending, I hope you come and hear me speak!
You can download Transfer from here.
I did a quick interview via IM with Judith Dinowitz on travelling over to the USA for
cf.Objective() 2007, and the talks I'll be doing on
Transfer, and some of the new features that Transfer will have in the next release. If you want to have a read,
check it over here.
Travelling
to cf.Objective() this year is going to be so much fun, I can't tell
you how excited I am to finally meet a whole heap of people that I've
only ever known online, not to mention all the great presentations.
Definitely well worth the over 24 hours of travel that I have to do to
get to Minneapolis.
Damon Gentry has started
blogging about Transfer over at his website
www.dagen.net.
He's done three articles so far as he logs his journey with
Transfer, and it is a very interesting read as he learns about the framework.
So, if you are interested in Transfer, or are looking to learn
something new, have a look at Dan's blog, it's got some good content.
If you take a wander over to the
cf.Objective() site, you'll see that my two presentations on Transfer are listed on the
session page.
I'm
going to be doing a general introduction to Transfer, as well as an
extended 'Advanced Transfer Techniques' presentation that will go into
some of the extra features of Transfer that aid in managing your model,
like caching, TQL, Decorators and Observable Events.
The session list is shaping up really nicely, and I've already earmarked several presentations that I definitely want to be at.
So far, my schedule looks tentatively like -
- Peter Farrell - Head First Mach II
- Peter Farrell - What's New in Mach II 1.5.0?
- Sean Corfield - Real World SOA: Building services with ColdSpring and Transfer
- Sean Corfield - Error Handling in service oriented Architectures
- Mark Drew - The CFEclipse Project
- Nicholas Tunney - JVM Server Tuning
Of course, I say this not knowing what times what presentations are, or even if I'm speaking during them. ;o)
cf.Objective() 2007 looks to be an amazing conference, so if you're on the fence about attending, I would highly recommend you
come down.
The
interview I did with Nick Tong as just been put up on
CFFramworks.com . I'm really happy with how it came out, so I hope you enjoy listening to it.
We had a good chat, mostly about
Transfer , but also about frameworks in general.
Let me know if you have trouble understanding my Australian accent. ;o)
I'm really excited that I am presenting at
cf.Objective this year. It was a bit of a last minute arrangement, but the tickets are almost all booked, and I'm looking forward to a long journey from my home in Australia all the way to Minneapolis in May.
I'm going to be doing a couple of presentations on
Transfer , which is going to be good fun, one of which will cover an introduction to Transfer and ORMs in general, and the other will cover some more 'advanced' Transfer development options.
It's also going to be fantastic to finally put some faces to names of people I've met through the community. I'm really looking forward to it.
See you all there!
After quite a few bug fixes and performance enhancements, it made sense to do a maintenance release of
Transfer ORM before moving on to version 0.7.
The major improvements and bug fixes that you will see in this release are:
- Documentation updates.
It was noted that some areas of Transfer were not documented as well as they should. You will now find a brand new 'Getting Started' section, as well as more details in sections such as Caching and Decorators.
- The caching layer now allows the underlying Java engine to discard TransferObjects as required.
This means that as the underlying JVM realises that it is requires more memory, it can discard TransferObjects that aren't being used. This ensures that Transfer won't be a memory leak.
- ModelGlue folder deleted.
You will now find the Model Glue : Unity integration code in the MG:U framework. For more details on how to integrate MG:U will Transfer, go to:
http://www.firemoss.com/blog/index.cfm?mode=entry&entry=F2BFB7B2-3048-55C9-4329FCB6EE13BAB3
- Multiple bug fixes and performance enhancements
Quite a few bug fixes and performance enhancements, mostly to do with the clone() operation, but a few other things as well.
For a full list of changes, you can see the
changelog .
Download Transfer 0.6.1
For a peek at the future, the major enhancements lined up for 0.7 are:
- Soft Deletes
Be able to specify a field in your configuration XML that denotes if a Object is deleted, and have Transfer mange it automatically for you.
- Composite Key support
Quite a few of you have asked for it, now you will get it!
- Transfer Query Language (TQL)
Another nice way to simplify doing gateway list() queries and read() operations, with a simplified SQL like syntax that follows the configuration XML file.
If you have any questions or comments there is a
mailing list , and a
forum .
Now my name is officially on the list of speakers , I can happily announce that I am talking at the 2007 WebDU conference in Sydney, Australia .
I'm very excited about this, as this is my first time presenting at a conference, and I think this is going to be lots of fun.
Un-surprisingly, I will be presenting on Developing Applictions with Transfer ORM , so please come down and hear me speak, I think you will find it interesting.
But even if you don't hear me speak, come down to the conference. I've been in previous years, and it's always a great time.
Hope to see you there!
After a bit of time in a release candidate, version 0.6 of
Transfer is ready to be released.
There is nothing very exciting to report, other than the fact that release candidate saw
some critical fixes , so I'm very happy with the choice to do that, and we will see a release pattern much like that in the future.
See the
Release Candidate post and full
changelog for the new features found in 0.6.
Transfer 0.6 Download
Next on the road map is:
- Composite Keys
- Multiple Configuration files
- Syntax in *where() methods to utilise cfqueryparam
- ...and some more stuff
Much thanks to all of those that tested out the Release Candidate, it was very much appreciated!!!
Please remember, there is a mailing list , and a forum , so if you have any feedback, please don't hesitate to send it in.
If you were unable to attend the Connect presentation yesterday that Brian Rinaldi organized for Transfer ORM, you can watch the recording here .
I haven't uploaded the slides, as I figured that the recording is far more valuable than just the slides by themselves. If you want the slides, let me know, and I'd be happy to post them somewhere for download.
I've also uploaded the sample application that I displayed during the presentation, and that can also be downloaded from here .
I have also updated the SVN version of the tBlog sample application (Much thanks to A J Mercer for doing the work on that) so that it works on v0.6 RC1. It still needs the setup SQL for Oracle, but I'm sure this won't be an issue for most people (anyone want to do that for me?).
All in all, I was very happy with the presentation, but if you have any feedback on it at all - from information presented, to presentation style, please feel free to post it in the comments or send me an email directly .
Thanks to Brian Rinaldi over as
RemoteSynthesis.com , I will be presenting via Breeze on
Transfer ORM on November 6th, at 4pm EST.
I'm planning on covering the basics of Object Relational Mappers, why they are around, and what problems they solve in developing applications.
From there we will look at Transfer, how it works, and some sample code.
If you have a chance, please come down and attend.
Full details of the talk, including the Breeze URL can be found
here .
I'm very pleased to announce the Release Candidate of 0.6 of Transfer ORM for ColdFusion.
Since there has been such a huge leap forward in terms of functionality between 0.5 and 0.6, I decided to run a release candidate for a short while, before releasing a final build of 0.6.
Any and all commentary is welcome on this release, not only bugs, and feature requests , but suggestions on documentation, API, and anywhere else you feel the product could be improved.
(Yes, I know the documentation is quite possibly the ugliest thing you have ever seen ;) hopefully before the full release of 0.6 I can try and convince my graphic designer girlfriend to do some branding for me.... But have you tried to explain an Object Relational Mapper to a graphic designer?)
Transfer 0.6RC1 can be downloaded from here.
Now, on to the fun stuff: What is new in this release, well, pretty much everything I talked about recently and a bit more.
Yes it is true! You can now use Transfer on Oracle!
Good for large, complex domains, compositional elements now have a 'lazy' attribute that can be set to true, e.g.
<object name="User">
...
<onetomany name="Permission" lazy="true">
...
</onetomany>
</object>
This means that the Permission objects won't be loaded until they are requested.
- Nullable properties on objects.
Now properties on objects can be set to a 'Null' value. A new 'nullable' property attribute adds two new methods: 'setPropertyNameNull()' and 'getPropertyNameIsNull()' which sets the value of the property to a configurable value that is designated to be NULL - be it an empty string, -123456789 or :::!!NULL!!:::
- Implemented the ability to 'decorate' TransferObjects, to provide greater customisation possibilities.
If you wish to write your own CFCs to be utilised by Transfer, you can. For example, if I had:
<object name="User" decorator="mysite.security.User">
...
</object>
When you run getTransfer().get("User", 1); you will get an instance of 'mysite.Security.User' which wraps around the original transfer.com.TransferObject and automagically extends all the public methods of that object.
Very handy if you need a high level of customization to your objects.
- Now able to turn off internal transaction blocks within Transfer with an optional argument on create(), update(), save() and delete() calls.
Now you are able to make calls such as getTransfer().save(user, false); and it will ignore the internal transactions - meaning you can wrap large commitments to the database in their own <cftransaction> blocks, and not have to worry about conflicts with transactions inside of Transfer.
- Fixed the misspelling of 'objectDefinitions' in the XSD file. Note: This could cause 'object not found' errors if you are updating from a previous version of Transfer. Make sure you validate against the new schema.
It took up until 0.5 to find this spelling mistake! If you are using a previous version, make sure your configuration file is validated against the schema (a good idea anyway), to ensure you don't get confused by the change.
- Cached objects will now automatically synchronise data if a non-cached object is saved and/or deleted.
This was a shortcoming of the previous version, in that you could discard objects from the cache, and you could set them to time out, but if an object that wasn't in cache was committed to the database, and there was a cached object in scope - the changes wouldn't be reflected in the cache.
Now these changes are synchronised with the cached object, so that the data in cache is not out of sync with the database.
- clone() method on all generated objects. Clone will syncronise with cached objects on getTransfer().save(), getTransfer().update() and getTransfer().delete() calls.
You can now create a deep clone of any Transfer generated object, which is not stored in the Transfer cache.
However, as we looked at previously, any change made to the cloned version will be synchronised to the cache if it is committed to the database.
This means you can make changes to an object outside of the scope of the cache, if you need to.
- Put condition on 'onetomany' and 'manytone', allowing for composition filtered by a condition.
You are now also able to setup conditions on which objects are included when having a structure or array of composite objects.
For example, you may wish to -
<manytomany name="Permission" table="lnkPermission">
...
<collection type="array">
<condition property="isActive" value="1" />
</collection>
</manytomany>
This will filter only active permissions.
- find() function on manytomany and onetomany composition.
A new generated 'find' function, which will return back the index of the object you pass in if the collection is an array, or the key it is stored under, if it is a struct.
- insert-ignore, insert-refresh, update-ignore, update-refresh attributes on properties.
These new property attributes can tell Transfer not to insert or update these property values when committing to the database.
The refresh properties can tell Transfer to go back to the database after a update or insert and get the value of the property, and update the Transfer generated object.
This is particularly handy for setting objects with database default values, or populating objects with values created from database triggers.
- listByPropertyMap() and readByPropertyMap() to allow for using multiple property values to do reads and lists of objects.
These two new methods allow for listing and reading objects by a combination of property values, simply by passing in a struct of key-value pairs into the function.
For example -
properties = StructNew();
properties.firstName = 'Mark'
properties.lastName = 'Mandel'
user = getTransfer().readByPropertyMap("User", properties);
- Columns are aliased with property names on list*() operations.
By default, all list*() operations will apply the property name set in the configuration file as aliases to the columns that are returned from a list query.
A new optional 'useAliases' attribute that allows you to turn off column aliasing if you need to.
- Transfer.getTransferMetaData(className) so that you are able to get the Transfer meta data on an object.
This meta data was exposed for the Model-Glue:Unity integration, but could be used for a variety of purposes.
- discardByClassAndKey(className, key), discardByClassAndKeyArray(className, keyArray), discardByClassAndKeyQuery(className, keyQuery, keyColumn).
These methods allow you to discard objects from the cache, if they exist. This is useful for clustered environments and for batch deletions from the database.
- XML Schema now contains documentation, so you get help with your code hinting.
The XML schema contains all the help documentation, so in your XML editor you should get the documentation to display along with your code hinting.
Incorporated the newest version of wwwObjectDoc into TransferDoc, which now gives us an ordered list of the methods on an object. (Thanks Aaron!)
- Bug fixes, refactoring and the like...
Also a big thanks to all those who contributed, helped out, gave me bugs, wrote blog posts, and anything else I can't think of right now, it is all very appreciated.
And if you haven't already, please join the mailing list .
Transfer has been officially moved to
RIAForge , at
http://transfer.riaforge.org/ Barring a few files that are still left on
cfopen.org , everything else is running through RIAForge. Those old files will remain on cfopen, until the 0.6RC1 release, which should hopefully be sometime this week, at which time I'll shut down the old cfopen.org project.
I love RIAForge, and I'm really excited about it. I think it's a fantastic boon for the community and I'm glad to be moving Transfer on to there.
Much thanks to those who helped bring this into being, you guys are great!
Brian Rinaldi over at
RemoteSysthesis.com has written up a short tutorial on
Transfer , including details on how he integrated it with the
ColdSpring .
It's a really nice introduction into using Transfer, so if you are interested in Transfer,
please go over and have a look .
Much thanks, Brian!
Things seem to have heated up in the news for
Transfer , which has been fantastic. Huge thanks to
Sean Corfield and
Matt Woodard for their write-ups on Transfer, they are greatly appreciated.
The most interesting of news is having
Sean on board to write a the ORM Adapter for
Model-Glue:Unity , so that Transfer can be used in place of
Reactor . This has actually been very interesting, as I have had very little experience with Model Glue, but with some enhancements and changes to Transfer, Sean got it up and running in what seemed to be relative ease.
The reason I haven't been able to do many posts about Transfer as of late, is because I've been attempting to get code out the door in preparation for the 0.6 release, but I figured with all the good press it has been getting lately, I definitely needed to get off my lazy rear end and put hands to the keyboard.
I have to say - there has been a lot of work done on Transfer, and I'm very happy with the new features that are coming out with the next release.
I won't take you through a full change log, but a teaser of some of the major features that are either already implemented in CVS, or will be soon, that you can look forward to:
- Oracle Support - Got some small bugs working on this right now.
- Lazy Loading of Composite Objects - About time too!
- TransferObject 'Decorators' - No more generic 'TransferObjects' if you don't want them. Decorate generated objects with real CFC's, and extend their functionality as you want.
- Tell Transfer to not use transaction blocks, during saves and deletes
- Nullable property values
- Refresh and/or ignore property values on insert or update
- Automatic data syncronisation with cached objects
- Place conditions on composition elements
- Heaps of bug fixes
The roadmap has also been set for the 1.0 release, seeing Transfer finally move out of its beta state, which I'm finding very exciting.
If you are at all interested in Transfer, I would suggest getting onto the
mailing list , and downloading the source from
CVS , and having a play with it.
If you have any questions, or problems, please do post them to the mailing list, or hit me via the meebome widget or don't forget there is now a #transfer channel on
Dalnet - I'm there almost every day, and am happy to help. My IRC handle is [Neurotic], so fire me a message if you hop online.
Previously we looked at doing basic CRUD operations in Transfer, and I was going to look at some of the composition options that are available, but I decided we would look at primary keys instead first.
Primary keys are an integral part of almost any database driven application, and consequently are an integral part of Transfer as well.
When a Business Object is inserted into the database, a primary key value will need to be supplied, either by the database generating the unique value, or by code generating the unique value.
A Business Object will need to know the value of its primary key, so that it knows what record it is representing.
Transfer will handle this process for you, and how it does that is configured through the <id> element that is found within the
Transfer configuration file .
There are three different ways that Transfer does this:
Retrieves Database Generated Primary Keys
By default, Transfer will attempt to retrieve numeric keys that are generated by the database. Understandably, this happens differently for each supported database -
- Microsoft SQL Server: Generates Identity value
Example: <id name="IDPost" type="numeric" />
This will retrieve the last inserted Identity key that was generated by SQL Server.
- MySQL: Generates 'auto increment' values
Example: <id name="IDPost" type="numeric" />
This will retrieve the last inserted auto increment value that was generated by MySQL
- PostGreSQL: Generates primary key values through sequences
Example <id name="IDPost" type="numeric" sequence="post_seq" />
This will retrieve the last sequence value that was by incremented by PostGres.
Transfer can Generate Primary Keys
Transfer can also generate primary key values for you. There are three different types of primary keys that it can generate -
- Numeric
Example: <id name="IDPost" type="numeric" generate="true" />
This will generate a unique numeric value for the table
- UUID
Example: <id name="IDPost" type="UUID" generate="true" />
This will generate a UUID value for the primary key
- GUID
Example: <id name="IDPost" type="GUID" generate="true" />
This will generate a GUID value for the primary key
Manually Override Transfer
Last, but not least, you are able to manually override Transfer's primary key controls to insert a record with a primary key value of your choosing.
A Business Object generated by Transfer will have a set() method for its primary key. If this value is set before the Object is inserted into the database, Transfer will attempt to insert it using this value.
For example,
<cfscript>
post = transfer.new("post.Post");
post.setIDPost("george");
transfer.save(post);
</cfscript>
This will attempt to insert the Post with the primary key value of "george".
With these three options, you should have all the control you need to manage primary keys within the database.
More details on Transfer can be found here .
For those of you who were at
Melbourne CFUG last night,
here are the presentation slides for you to download, along with the example application I showed.
For those of who weren't there, (being asleep and half a world away is no excuse) feel free to have a look at the slide presentations, I hope that you will find them useful.
The presentation went very well, albeit over time, and was well received, so I was quite happy over all.
If anyone is interested in me doing this presentation to a CFUG or related, most likely over Breeze as I am in the rear end of the world, please
drop me a line , I'd be delighted.
I just uploaded the example
Transfer Pet Market application to
cfpetmarket .
Besides changing the data management aspects to utilise Transfer, the application has stayed fairly true to its original form, and should hopefully provide a good example of some of the things that Transfer can do.
Some of the new functionality in 0.5 was a result of the issues I faced while developing the pet market application, so it was a good exercise both in providing an example for Transfer, and also testing out what limits it had.
So please wander over to the
cfpetmarket , and have a look, and feel free to leave me any feedback or questions that you may have either
here ,
here or
here !
On the 22nd of June I'll be doing a short presentation on Transfer and it's capabilities at the Melbourne ColdFusion Design and Development User Group.
I'll be covering the basics of what an ORM actually is, what Transfer can do, and be showing a small sample application.
If you are at all interested, I would love it if you would come down and listen.
Details can be seen here.
It has been over a month since I released 0.4 of Transfer, and there has been a lot of work on Transfer been done to get it ready for 0.5.
First of all, I'd like to extend a big thanks to my current employers, NGA.net , who allowed me to open source the development on Transfer that was required for a current project. You guys are great.
Second of all, I would just like to say - I've put a lot of time and effort into this release, so I would greatly appreciate it if you could take some time to download and give it a good test and general bash around. If you want to provide feedback there is a mailing list , a forum , a bug tracker , and a feature request tracker , and failing all that, you can send me an email directly!
I want to hear your feedback, whether it is good, bad, or otherwise. One of the initial reasons I started writing open source code was as a learning exercise (one of which I recommend quite highly), so your feedback only helps in that process.
With this release, I have written a cfpetmarket application using Transfer. All I have yet to do on it is write the readme, and bundle it up, so it should be released in day or two.
There is also the tBlog example application that has been updated to use the new code.
On to more important things however - What's new in this release?
- Postgresql support
I've implemented PostGres support, and included setup SQL with the tBlog application for it. I haven't spent much time with PostGres as a database, so anyone who uses it regularly and wants to test out how Transfer handles on it, please do.
- The ability to control the caching of objects within Transfer, down to an object and scope level.
While many people will probably never need this functionality, you can now configure the caching of Objects down to numbers, time, as well as pushing the cache to shared scopes like request, session or application. Very useful if you have multiple copies of Transfer running in a Application, but you want to share caches.
- Now getIsPersisted() and getIsDirty() methods on TransferObjects to determine if an object has been persisted in the database and/or had it data modified respectively.
This was more for internal use within Transfer, but it is handy to be able to see if objects have been modified and/or have been created in the database.
- If Object have not been modified since their last commitment to the database, the Transfer.update() will not fire events, or run the database update.
This is useful if you are writing cascading save() calls on composite objects. You can now safely call save() on an object, knowing that it will only hit the database if the data has been changed.
- TransferDoc
First off, thanks to much thanks to Aaron J Lynch for allowing me to use and modify his wwwObjectDoc tool for this. This is a tool for generating documentation on the Business Objects that Transfer creates for you. I use this a lot when using Transfer, as I can't remember what property of onetomany relationship has created what.
- If the primary key has been set before inserting, it overrides Transfer's population/retrieval mechanism, and the object is inserted under the set primary key.
This is something I came across when I was writing cfpetmarket - I needed to override all the primary key handling of Transfer, and manually set it. So now, if you use the setter for the Primary Key on a TransferObject, Transfer will try and insert the record with that primary key, rather than try and retrieve it from the database, or try and generate it.
- Some better handling of database 'null' values being turned into values like dates, GUIDs, numbers etc (Not much really you can do to emulate 'null' however).
This was a bit of a bug. Transfer didn't handle NULL values coming out of databases very well. Now NULL values result in default values.
- onetomany relationships can now removeParent, and hasParent relationships.
- manytoone relationships now have has, and remove functions.
I'll lump these two together. Now you can have compositions that may or may not have objects in them. If the object is not set, Transfer will attempt to insert/update NULL values into the relevant foreign keys.
- Removal of the use of a preparedStatement on Selects, and reverted back to CFQUERY, which is much more performant on memory.
Originally Transfer used a Java preparedStatement to run its SQL queries. Looking at this again I realised it wasn't necessary, so I moved it back to cfquery, and found a performance increase.
- Fixed bug where composite objects more than 2 levels deep would fail
This was a nasty bug, and has now been fixed. I wrote some code that made no sense.
- Fixed bug where composite objects with the same named columns would fail
This was also a nasty bug, as has also been fixed.
There are a few more bits and pieces behind the scenes - but a full change log can be seen
here .
On to new and better things - what can you expect in the next release?
- Oracle Support
I wanted to get this out in the last release, but I had a need for the caching controls, so that took precedence. The hooks are in place to write this code, it just needs to be done and tested.
- Lazy Loading
I've been bugged and bugged for this functionality, and I've finally realised that this really needs to be in there. In some ways I wonder if I went the wrong way by not implementing this in the first place, but my software design is proving to be quite flexible, and I have quite a few ideas how to get this ball rolling.
- Cascading creates, updates and saves.
This would be really handy, so I'm going to do it.
There are also a few other bits in the wind. A full todo list can be seen
here . I am also totally open to ideas, so if you think of anything, please put it in the
feature request tracker .
Now that this has been released, I can write up the rest of my blog posts on Transfer, and I also have a CFUG presentation on Transfer coming up which I will make the contents of available for download.
And of course - this website runs on Transfer 0.5 right now.
Transfer 0.5 can be downloaded from here .
I have been slowly working my way through making a Transfer version of cfpetmarket , which has been a great deal of fun.
What I propose to do with this series of articles is to go through some small cases in the cfpetmarket application, as it will eventually be released, and use it as a base to explain some of the functionality of Transfer.
This may end up being a repeat of some of things I've talked about before with Transfer, but I think some small things have changed, so I'm happy to go over them again.
First off all, we'll need to setup Transfer, which is pretty straight forward. Simply place the 'transfer' directory in the root of your web directory, or you can make a '/transfer' mapping to the directory if you want to keep it off the web root.
From here, we first need to create two XML files.
The first is where we set what our data source definition is, I like to call mine 'datasource.xml', but realistically you can call it whatever you like.
The datasource.xml for cfpetmarket looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<datasource xsi:noNamespaceSchemaLocation="../transfer/resources/xsd/datasource.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<name>petmarket</name>
<username></username>
<password></password>
</datasource>
You will notice there is a reference to a datasource.xsd file. I use Eclipse as an IDE, and when I create my XML file, I refer it to an xml schema file, in this case datasource.xsd, so I get my context suggestion and xml validation.
This should look pretty straight forward, where the 'name' element is the name of the datasource, and the 'username' and 'password' elements are for optional username and password fields for the ColdFusion datasource.
The second file we create is a 'transfer.xml', this defines each of our Transfer Objects in our application. Again, it doesn't have to be called 'transfer.xml', but since that's the name of the framework, I figured it was appropriate.
Again, we will reference an XSD file, and in this case it will be the 'transfer.xsd' schema. Normally we would have several 'object' and 'package' elements, but we'll add them in a little bit, so for now, all we'll worry about is:
<?xml version="1.0" encoding="UTF-8"?>
<transfer xsi:noNamespaceSchemaLocation="../transfer/resources/xsd/transfer.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<objectDefinitions/>
</transfer>
Normally we would have our package and object definitions inside the 'objectDefinitions' element, but we'll take a look at the database first.
We have to create a singleton of the TransferFactory, so we can actually use Transfer. Singleton is just a fancy shmancy way of saying 'there is only of these in the application'. The easiest way to do this is to create it in the application scope.
This is probably the least sophisticated way of handling a singleton, but for the purposes of this article (and in cfpetmarket) it will work well for us.
The TransferFactory takes 3 arguments
- The relative path to the datasource.xml file
- The relative path to the transfer.xml file
- The path from root to where Transfer is going generate it's definition files
The first two are relatively straight forward arguments, the third may not be so.
The way Transfer works is by generating ColdFusion code the first time it is run. Once that code is generated, it is then run and utilised in generating the Transfer Objects that Transfer creates. Therefore, Transfer needs to know this directory both so it can generate the required files, and also so that it can find them later on.
An example initialisation would look like this:
application.transferFactory = createObject("component", "transfer.TransferFactory").init("/config/datasource.xml",
"/config/transfer.xml",
"/config/definitions");
In this case, we will be creating the definition files in the '/config/defintions' folder off the web root. The definitions don't have to be created here, but it seemed like a logical place for the files to go.
Now that we have our TransferFactory up and running, we'll look the database.
In this tutorial, as all we are looking at is Create, Read, Update and Delete (CRUD) operations, we'll only be looking at one table in the database, and there are more coming.
The table we are looking at is the 'category' table. The category table looks like so:
| |
categoryoid
|
int |
PK
|
displayname |
varchar
|
| |
systemname |
varchar |
| |
color |
varchar |
| |
locale |
varchar |
| |
image |
varchar |
It should be noted that we are dealing with a MS SQLServer database, but it is possible to use other databases with Transfer.
It should also be noted, within the actual cfpetmarket application there is no Identity seed on the 'categoryoid' column, but for the sake of this article, we're going to pretend that there is.
So let's go back to our transfer.xml file, and we're going to create a 'package' inside this definition. Packages are just like regular packages, in that they are a means to organise all our objects into separate areas for ease of use.
We'll create a 'pets' package for our 'Category' to eventually sit under, as it is a Pet Store Category.
Creating a package looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<transfer xsi:noNamespaceSchemaLocation="../transfer/resources/xsd/transfer.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<objectDefinitions>
<package name="pets">
</package>
</objectDefinitions>
</transfer>
Packages can contain packages, so you are not limited to how many levels you are able to go.
Next we have to define an object, and tell it which table it corresponds to. We're going to call our object 'Category' and point it to the 'category' table
<?xml version="1.0" encoding="UTF-8"?>
<transfer xsi:noNamespaceSchemaLocation="../transfer/resources/xsd/transfer.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<objectDefinitions>
<package name="pets">
<object name="Category" table="category">
</object>
</package>
</objectDefinitions>
</transfer>
Next we're going to tell it which column is the primary key on the table -
<?xml version="1.0" encoding="UTF-8"?>
<transfer xsi:noNamespaceSchemaLocation="../transfer/resources/xsd/transfer.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<objectDefinitions>
<package name="pets">
<object name="Category" table="category">
<id name="categoryoid" type="numeric"/>
</object>
</package>
</objectDefinitions>
</transfer>
You will notice that we haven't specified what the column is in the 'id' element. There is a 'column' attribute to the 'id' element, however it's default value is the value of the 'name' attribute, and in this case, the value of 'categoryoid' is perfect for the name of the 'id' property.
Next we will look at the properties of the 'Category' objects, and strangely enough, these are pretty much exactly the same format as the 'id' element, except that they are 'property' elements.
<?xml version="1.0" encoding="UTF-8"?>
<transfer xsi:noNamespaceSchemaLocation="../transfer/resources/xsd/transfer.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<objectDefinitions>
<package name="pets">
<object name="Category" table="category">
<id name="categoryoid" type="numeric"/>
<property name="displayname" type="string"/>
<property name="systemname" type="string"/>
<property name="color" type="string"/>
<property name="categoryLocale" type="string" column="locale"/>
<property name="image" type="string"/>
</object>
</package>
</objectDefinitions>
</transfer>
As you can probably see, each 'property' element corresponds with a column on the 'category' table. On the last item, we've decided we wanted to have a different property name than the name of the column, so in this case, we've called the property name 'categoryLocale' and the column is set to 'locale'. The reason for this will become clearer later on.
So now, if we reset our initialisation on the TransferFactory, so that it re-reads our new transfer.xml file, we have ability to create, read, update and delete records on the 'category' table.
So from here, if we want a new 'Category' object, we first have to get an instance of Transfer from the TransferFactory -
<cfset transfer = application.transferFactory.getTransfer()>
And then we request the object
<cfset category = transfer.new("pets.Category")>
It should be noted that the class name is case sensitive. So in this case, looking for a class called 'pets.category' will result in an error.
Now we have an object of type 'transfer.com.TransferObject'. But if you look at the API for a TransferObject, there isn't much to it that allows you to do much with the Object itself. That is because Transfer adds the required methods to the TransferObject at run time, so that you can manipulate the object.
So for each property and also for the id, there is a get and set method for each of the names of the property. i.e. There is now a 'setCategoryOID', 'getCategoryOID', 'setDisplayName', 'getDisplayName', 'setColor', 'getColor' etc methods on the object for us to use.
Now you may also be able to see why we created the property named 'categoryLocale' and mapped it back to the column 'locale'. The reason for this is that 'setLocale' is a ColdFusion native function, and you cannot create a function with the name 'setLocale', otherwise the system will throw an error. Therefore we had to have a different name for the property other than 'Locale', otherwise there would have been a issue.
So if we want to set some properties of the Category before we insert it into the database, we can use the setter functions on the object, like so:
<cfset category.setCategoryName("Fish")>
<cfset category.setImage("fish.jpg")>
<cfset category.setColor("#fc3e41")>
And so on.
Now, the easy bit comes - inserting it into the database:
<cfset transfer.save(category)>
And that is it. It has now been inserted into the database, and the 'categoryOID" property on the 'category' object has automatically been set to the ID that the database would have created.
Now if we decide we want to change the image on the category, we can now do:
<cfset category. setImage("fish2.jpg")>
<cfset transfer.save(category)>
And the category has been updated in the database. The 'save' command intelligently calls the appropriate 'INSERT' or 'UPDATE' command in the database, depending on whether or not the object has been committed to the database yet.
If you want to retrieve the category object from the database, you only need to tell Transfer the class that you want, and the id of the category you wish to retrieve, like so:
<cfset category = transfer.get("pets.Category", 1234)>
Again, note that the class name is case sensitive.
And to finally complete the set, if you want to delete a Category -
<cfset transfer.delete(category)>
And the Category has been deleted from the database. Nothing more than that.
That pretty much covers all the aspects of getting creates, reads, updates and deletes happening on your database with Transfer.
Don't forget more details on Transfer can be found at the home page, as we've only just scratched the surface, and if this interests you, please feel free to join the mailing list.
Next article we'll start looking at composite objects and how they are handled in Transfer.
Its been a long time in coming, but version 0.4 of Transfer has finally been released.
This version sees Transfer finally get some placed on a CVS repository, and also take advantage of its place in cfopen.org
The main new features for this version are:
- If an object does not exist from transfer.get(); it now returns an empty object, rather than throwing an exception. This is more in line with other ORMs.
- readByProperty() and readByWhere() methods added to transfer.com.Transfer to retrieve objects by non-primary Key values.
- list(), listByProperty() and listByWhere() functions added to transfer.com.Transfer to provide simple listing of table values.
- property[@column] and id[@column] in the config xml now default to the value of the name, which should result in less typing.
- Added auto generation of GUID's that conform to MS standards, and are performant for indexing. XSD updated XSD accordingly.
- Able to set a 'configure' custom function that is run just after the init() function is run.
- More documentation on new features
The full changelog can be viewed
here .
Transfer can be downloaded from here .
The next version is going to focus on getting support working for several more database types, top priorities being postGres and Oracle.
I also plan on doing a series of tutorial articles on this blog to show how to take advantage of Transfer and all its functionality.
It's been great fun getting my hands dirty in Transfer again, so I hope you enjoy the efforts.
I think to myself today... gee, I haven't checked out my project application on cfopen.org for aaages, I'll log in and see what's going on.
Lo and Behold! The accepted my project back in November 2005, and I had no idea.
Thank you very much to the guys at cfopen, I must have lost the email along the way somewhere! Guess that shows me that I should be more diligent in paying attention.
So I'm slowly moving my files up to CVS on cfopen - which is a long time in coming for Transfer . At the moment it's just the core code, but I will also be putting up my test cases, and the example code base shortly.
Version 0.4 is just on the horizon, with me pretty much just left with making some minor edits to some files, and packaging it all up nicely left to go, but I figured I would let you all have a taste a little bit early.
Enjoy the new CVS Repository.
Version 0.3 of Transfer ORM is now ready for download .
New things to look for in this release:
- The 'generate' attribute on the ID element - now you have to specify if you want Transfer to generate primary keys for you. Otherwise it defaults to the new ability to retrieve database generated primary keys.
- Primary keys are now set to default values.
- A new method on the Transfer class - save(). This intelligently will either create, or update the transfer object dependent on its current state.
- If you are using Transfer to create your primary keys, you no longer need to create the transfer_sequence table. Transfer now does this behind the scenes.
- All the bugs with UUID primary keys have been fixed.
- The transfer.xsd has been fixed up so that attributes that should have been set to 'required' are now.
There are a few more pieces, but they are all outlined in the change log and the documentation .
The tBlog example application has also been updated to use the new functionality, and can be downloaded from here.
Please do not forget that there is a mailing list for Transfer, and any and all feedback is appreciated.
Version 0.2 of Transfer ORM library is now ready for download and there is a whole lot of new things to look at.
It's well past midnight here, so I'll go into full detail about this in coming days, but some details of what I've been working on with this version:
- A new area of the site dedicated specifically to Transfer
- A lot more documentation
- Installation is now easier, with the 'transfer_sequence' table no longer needing you to define it's data - it is now handled by Transfer.
- The ability to define your own CFML functions on a Business Object in the Transfer XML configuration file.
- A new mailing list for users of Transfer for the discussion and support of Transfer.
- Updated the example application to incorporate these new features, and include the latest features and new functionality.
I've also sent off a application to cfopen , and hopefully when that gets approved, I will be moving the codebase into CVS on there, not to mention it will be nice to have a centralised bug tracker.
However, I am asking of all the people that downloaded Transfer, or plan to soon (yes, I read my web stats, there were a few of you), is to please, join the mailing list, and then have a good go at testing Transfer for me.
While I had a lot of fun testing Transfer out myself, there are always things the developers never think that someone will do with their software, so I figure there has definitley got to be holes out there for someone to poke in this.
If you have some time, please do test Transfer on supported environments, unsupported environments, and anything else that you feel like, and let me know what happens.
Transfer is a library of CFCs for an idea I had a long time ago, and now I am going to pass it out to you for your feedback, and see if what I have developed is sometime useful that should be further developed. This is the first time I've ever released anything substantial into the wide space of the Internet, so please bear with me.
The thought for Transfer was that to ultimately cut back on development time, it would be very handy to simply be able to create your database, and a corresponding xml file, and from there your Business Objects could be created, updated and deleted all without the writing of another piece of code as well as being managed in persistant scopes.
This is done through 2 processes -
- The business object is manufactured within the library, but unlike a code generator, in which you create your code only once, and the CFCs are defined until you run your code generator again, Transfer works by generating only the udfs that are required by each business object, and decorating a generic TransferObject with the required functions at run time when the particular object is requested. This allows for easier development as the code generation is completely implicit, you don't need to manually fire it off.
- The Transfer Library automatically generates all your SQL for CRUD operations, based upon the details specified in the xml config file. This includes SQL for composite objects.
To show you how it works, I'll take you through some basic Transfer Object definitions, and then we'll get a little bit more complicated with some composite object creation.
This is taken from the provided tBlog example application that is available for download here.
If we want to have a User in the system, first we will set up a table that has the following details:
| Name |
Data type
|
IDUser (PK)
|
numeric
|
user_Name
|
varchar(500)
|
user_Email
|
varchar(500)
|
Within our transfer configuration file, we want an object of type 'user.User', and we tell it what table it is from.
<objectDefintions>
<package name="user">
<object name="User" table="tbl_User">
</object>
</package>
</objectDefintions>
We then define the primary key, which will give us a getIDUser() and setIDUser() on the eventual TransferObject
<objectDefintions>
<package name="user">
<object name="User" table="tbl_User">
<id name="IDUser" column="IDUser" type="numeric"/>
</object>
</package>
<objectDefintions>
We then set each of the properties on the 'user.User', in this case, name and email, giving us both get & setName() and get & setEmail() on the user.User TransferObject. Each property has a type, and requires that you specify which column that the property refers to.
<objectDefintions>
<package name="user">
<object name="User" table="tbl_User">
<id name="IDUser" column="IDUser" type="numeric"/>
<property name="Name" type="string" column="user_Name"/>
<property name="Email" type="string" column="user_Email"/>
</object>
</package>
<objectDefintions>
I won't take you into installing the Transfer library, I'll leave that to the (relatively sparse) documentation and the provided example, but we'll assume that the main class transfer.com.Transfer is available to you.
To retrieve a new empty user.User transfer object, all is required is:
<cfscript>
//get a new user
user = transfer.new("user.User");
</cfscript>
To then create the user as a record in the database, we can now:
<cfscript>
//create the user
user.setName("Mark");
user.setEmail("notanemail@email.com");
transfer.create(user);
</cfscript>
To retrieve a record from the database:
<cfscript>
//get a user
user = transfer.get("user.User", url.IDUser);
</cfscript>
Update and delete
<cfscript>
//update a user
user.setName("Fred");
transfer.update(user);
//delete
transfer.delete(user);
</cfscript>
A lot of this is relatively straight forward, however where Transfer really comes into it's own is where it starts handling composite objects.
There are 3 different types of ways in which Transfer can handle different composition of objects, but I will show you one example, so you can get a feel for how it works.
Again, from the tBlog example, we'll look at a Post with Comments (in tBlog, a post has Comments, a User, and multiple Categories, but in this case, we will only worry about Comments).
So first we create a 'post.Post' (I won't show the database table, I figure you have a grasp on how it works), and a 'post.Comment' objects:
<package name="post">
<object name="Post" table="tbl_Post">
<id name="IDPost" type="string" column="IDPost"/>
<property name="Title" type="string" column="post_Title"/>
<property name="Body" type="string" column="post_Body"/>
<property name="DateTime" type="date" column="post_DateTime"/>
</object>
<object name="Comment" table="tbl_Comment">
<id name="IDComment" type="numeric" column="IDComment"/>
<property name="Name" type="string" column="comment_Name"/>
<property name="Value" type="string" column="comment_Value"/>
<property name="DateTime" type="date" column="comment_DateTime"/>
</object>
</package>
This provides us with our basic CRUD operations for the tbl_Comment and tbl_Post, however in the database schema, tbl_Comment has a foreign key, lnkIDPost, that provides a one to many relationship between Post and Comment that we still need to define.
Strangely enough, we do it with an element in <object> called 'onetomany', like so:
<package name="post">
<object name="Post" table="tbl_Post">
<id name="IDPost" type="string" column="IDPost"/>
<property name="Title" type="string" column="post_Title"/>
<property name="Body" type="string" column="post_Body"/>
<property name="DateTime" type="date" column="post_DateTime"/>
<onetomany name="Comment">
<link to="post.Comment" column="lnkIDPost"/>
<collection type="array"/>
</onetomany>
</object>
<object name="Comment" table="tbl_Comment">
<id name="IDComment" type="numeric" column="IDComment"/>
<property name="Name" type="string" column="comment_Name"/>
<property name="Value" type="string" column="comment_Value"/>
<property name="DateTime" type="date" column="comment_DateTime"/>
</object>
</package>
In this, the name of the onetomany relationship is defined, thus giving us methods like 'addComment()', 'getCommentArray()', 'removeComment()' and a few more on the post.Post TransferObject. The onetomany element also sets a 'setParentParent()' and 'getParentPost()' method on the Comment itself, for controlling what post the comment is for.
If we want to create a new Comment, and add it to a post, all we need to do now is:
<cfscript>
post = transfer.get("post.Post", url.id);
comment = transfer.new("post.Comment");
comment.setName(form.name);
comment.setValue(form.comment);
comment.setParentPost(post);
//no need to sort, or add to the parent, it is done implicitly.
transfer.create(comment);
</cfscript>
And presto, the comment is created, and has implicitly been added to the Post.
If we were to go through the post.getCommentArray() we would find the new Comment in there.
You will also note that there is a 'type' attribute of 'array' value on the 'collection' element. This states what sort of collection is going to be created on a post.Post. The other option is 'struct', in which case you must also set a key to be used.
The advantage of having an array however, means we can control the sorting of the Comments, like so:
<object name="Post" table="tbl_Post">
<id name="IDPost" type="string" column="IDPost"/>
<property name="Title" type="string" column="post_Title"/>
<property name="Body" type="string" column="post_Body"/>
<property name="DateTime" type="date" column="post_DateTime"/>
<onetomany name="Comment">
<link to="post.Comment" column="lnkIDPost"/>
<collection type="array">
<order property="DateTime" order="asc"/>
</collection>
</onetomany>
</object>
This does several things - when a post is first retrieved from the database, all its comments will be sorted by their DateTime of entry, however when a new Comment is added, it is automatically software sorted into the correct order.
Therefore, if during development you have decided that sorting shouldn't be on the DateTime property of a post.Comment, but instead on the Name property, all you would need to do is:
<order property="Name" order="asc"/>
and it the change ordering would be handled the next time the xml file is read by the library.
There is a lot more to Transfer that I haven't looked at here, but if this has piqued your interest, the following is available for download:
I'll also make available the latest documentation as I write it at:
Which will get updated whenever I have a spare moment.
If you are going to have a look at what I've written, please do drop me a note, either via the comments below or via my contact form, I would greatly appreciate it. I've learnt a lot writing this library, so hopefully you can get something out of it as well.
Please note, this is pretty much Alpha code. While it does run behind the scenes here, it hasn't had nearly as much testing as is reasonable to run on a mission critical system, so in any way you use it, it is at your own risk.