For a long time I’ve been meaning to try out the the
IntelliJ CFML plug-in
for two reasons. One of which, I’ve always heard really good things
about the IntelliJ platform for editing Java, Groovy and Flex, and also
because as a Linux based ColdFusion developer, my CFML IDE options are
limited.
So I decided I was going to get some work done on
JavaLoader,
to move it into a 1.0 Final, and I was going to do it all in IntelliJ
with the CFML plugin, and use that as a way of seeing how the CFML
plugin was going.
Installation
Installation was the biggest hurdle for me, as I ran into a lot of issues getting the plug-in installed and running.
From
what I could see, IntelliJ makes no mention that you need to set a
JDK_HOME variable in your environment. Except when you first run it and
get an error message. Once that was done however, it fired up with no
problems at all.
For
whatever reason, after running IntelliJ, the CFML plug-in didn’t show
up in the plug-in list. If it had, I’m sure things may have gone a lot
smoother for me.
I then browsed to the
plugin page, to download the plug-in manually. Two things went wrong at this point.
- As far as I could see, IntelliJ does not describe anywhere how to install a plug-in manually. This
is the only documentation I could find on installing a plug-in, and it
was via the plug-in manager (see above), so it was not very helpful. It
is very simple, you just unzip it and put it in the /plugins folder.
- I
downloaded the wrong plug-in for my version on IntelliJ. I downloaded
9.03 Ultimate edition, or if you look in the build.txt file that comes
with IntelliJ, that’s Build IU-95.429. Unbeknownst to me it’s that build number that is used on the plug-in download page (See Since build and Until build )
that define what a plug-in is compatible with. Honestly I am confused
as to why they don’t just use the IntelliJ product number, but maybe
there is a good reason.
(Apparently this has since been fixed, and you can now install from the plug-in manager again)
Appearance
My
first thought when opening IntelliJ was “Wow, this is ugly”. I’m used
to working in Eclipse, and the Linux build of Eclipse takes the Ubuntu
theme that is currently set very nicely. So much so that people at
conferences often ask me how I get my Eclipse to look the way it does.
In IntelliJ, the text wasn’t even anti-aliased.
I quickly found an Appearance
setting, and switch the theme over to GTK+, which I expect made
IntelliJ use Ubuntu native GTK rendering engine, and suddenly IntelliJ
matched my Ubuntu theme, and was much more pleasant to look at. Eclipse
still looks more polished, but maybe that’s just because I’m used to
it.
Documentation
This
is where things fall down, unfortunately rather hard. The only
documentation for the CFML plug-in is a blog that can be found
here and a
google group .
This project is in desperate need of a website/wiki/Google
doc/anything that has some sort of structure. Skipping through blog
posts and searching the google group in an attempt to find that one
piece of knowledge you need is frustrating, to say the least.
Creating a Project
I
go to create a project, and... there is no ColdFusion Project. Uh oh.
Fire off an email to the group and ask “What do I do now?”. (Update, I
just got told to create a Java project. Weird, but it works).
In
the mean time, I switch tacks, and import an existing Eclipse Project.
IntelliJ asks me for a JDK, but is happy enough when I skip over it,
figuring I wouldn’t need one for a CFML project. That worked quite
well, and I’m now on my way to editing ColdFusion.
Editing Files
Code
completion for variables and functions inside a CFM and/or CFC is one
of the biggest priorities for me, and in the plug-in, I have to say,
this feature works very well.
That being said, one thing that is missing for variable completion for me, is completion for variables such as arguments , application , server , CGI , etc, for which there are
already bugs for. That and CFC resolution is missing, which Builder does for things like createObject() etc, (somewhat).
Interestingly
enough there is no “New CFM” or “New CFC”, there is only “New File”,
which I didn’t find myself missing, except for when creating CFCs. The
new CFC dialogue in builder is great, and if you’ve ever worked with
interfaces, having Builder generate the method stubs for you is pretty
cool as well.
Overall,
the responsiveness is very snappy, and code completion comes up almost
instantly, which is very nice. I prefer the function hinting in IntelliJ
to Builder, as it give you the hint per parameter as you are inside the
method, where as Builder only tends to give you the tooltip when you
first write the method.
While I didn’t get to play with it, there is also support for
code completion of Java objects , including
when being used with JavaLoader , which is not something ColdFusion Builder can do.
Live Templates
This
is more of a IntelliJ thing, but Live Templates blow CF Builder/Eclipse
Snippets out of the water. Wow. I love being able to define a $FOO$
variable in my snippet and declaring that $FOO$ variable to
complete().
IntelliJ then intelligently provides all the code completion that would
normally show up if I hit ctrl+space at that point. I think I’m only
just scratching the surface of what is possible with Live Templates, but
this feature is fantastic.
Learning Curve
IntelliJ
does some things differently to Eclipse, which takes a little while to
get used to. That being said, you can switch the keyboard commands to
Eclipse style with a menu change (I did some extra tweaking above and
beyond that), so that is nice.
Price
The
CFML plug-in only works on the commercial version of Intellij. Licences
for personal use carries a price tag that is fairly similar to
ColdFusion Builder. Corporate licences cost a fair amount more. For the
functionality provided by the CFML plug-in at this stage ,
I would recommend that if you are on Mac/Win, stick with Builder. You
will get more bang for your buck. If you are on Linux, and refuse to
move OS’s (like I am), IntelliJ is well worth an evaluation, as the
price tag is very reasonable, and if you are looking to do Groovy, Flex
or Java work, IntelliJ already has that functionality bundled in.
Overall
I
have to say, once I got over the initial hurdles of installation, and
documentation, and became more comfortable with IntelliJ as a whole, it
because a really nice IDE to work in. There are plenty of rough spots
around the edges, but I think with a few more revisions, this plug-in
could easily be a decent contender for ColdFusion Builder (especially if
it gets a debugger).
If
you are looking to purchase an IDE, I would still say stick with
ColdFusion Builder for now if you are on Mac/Windows, but if you are on
Linux, and don’t want to move, IntelliJ looks like a dark horse to watch
in the ColdFusion IDE space.
Learning More
If this has piqued your interest, the best place to really learn about this plug-in is the
google group, so sign up there.
The blog is also good for information about new releases and what new features are available.
Our wonderful sponsor Fasthit
has kindly provided us, not only with a ticket to give away to a
student living in Australia or New Zealand, but is also paying for
flights and accommodation as well for the lucky winner.
The rules are simple:
- You must be an Australian or New Zealand Resident
- You have to have a valid student identification.
- You must be under 25 years of age.
- You need to send an email to contest@cfobjective.com.au, with no more than 200 words stating why you would be the best person to win this competition.
...and that is really about it.
The winner with the best entry will be announced on the 1st of September.
If
you are a student, make sure you enter for a chance to win! If you
aren't a student, but know someone that is, make sure to pass on the
message.
For more details, have a look at this
news post.
There has been so much going on with
cf.Objective(ANZ), that I haven't even had time to blog about it!
In case you missed the announcements, the
pricing is now online, with the early bird price being lower than last years, at $749.00 including GST, valid until the 31st of August.
There are a series of
1 day workshops the day before the conference as well, covering ColdFusion Performance tuning, Flex 4 and ColdFusion, and Arduino hardware with ColdFusion.
Charlie Arehart,
Kai Koenig, and
Justin McLean are running these workshops respectively, and you couldn't ask for better instructors.
While each track of the 2 day conference has yet to be finalised, almost all of the sessions that will be available are
now online , and we have some fantastic content again this year, with some speakers that I am very excited about. The usual slew of amazing ANZ dignitaries will be joining us, also with international speakers such as
Gert Franz,
Mark Drew,
Mark Blair,
Mike Brunt and
Tim Buntel (Or maybe 'semi-international' would be a better fit? Mark Blair moved to San Francisco and then came back, Tim Buntel now lives in Sydney... ;o) ).
I'm really happy with the way this conference is coming together this year, and providing some really unique ColdFusion content for the ANZ region. We've got some great training, some great speakers, some great sessions, it's looking like it's going to be a fantastic time all around.
Bob Silverberg and I are back at it again, with a one day pre-conference lab at
Adobe MAX this year covering an introduction to Object Relational Mapping in ColdFusion 9.
As the description states:
Learn about one of the most compelling new features in Adobe ColdFusion 9 from two seasoned ColdFusion veterans. This release includes the integration of Hibernate, an enterprise-class, Java based ORM. Join us to hear how to get started using ColdFusion 9 ORM features in your next project. Through interactive learning and hands-on labs, we'll start with the basics and work together to add features to an application that takes advantage of the ORM. Then we'll discuss best practices and potential pitfalls, making this an ideal combination of introductory material and tips and tricks.
If you are interested in getting your feet wet with ColdFusion 9 ORM, you can get more information on the
MAX pre-conference page .
Hope to see you there!
In ColdFusion 9, several Web Services come pre-built with the product, allowing greater interoperability between different technologies and ColdFusion.
The other day saw the release of my Dzone RefCard outlining how you could take advantage of these Web Services from Java, and use ColdFusion's
If you are doing Java development, and are looking to integrate some of ColdFusion's capabilities into your application, you can
download the RefCard from here . It will step you through setting up the Services, generating the required Java code to run the Web Services, and also provides examples of usage to help you get started.
You can also read more about
ColdFusion Web Services here .
On 6pm US ET, Wednesday May 19 I will be presenting
Dependency Injection Redefined - ColdSpring 2.0 to the
CFMeetup Crowd.
For those of us in more southern parts of the world, that is 8:00am, the 20th of May, Australia.
This is the same talk I recently gave at
cf.Objective() and
WebDU .
Synopsis:
ColdSpring 2.0, codename Narwhal, is a complete rewrite of ColdSpring aiming is to provide a far more extensible architecture and many more features above and beyond what is currently provided with ColdSpring to date. These new features will give developers the capabilities to build and manipulate this Inversion of Control framework in fascinating and very powerful ways, thus saving them even more time when managing their dependencies and utilising functionality such as Aspect Oriented Programming.
In this presentation we will look at the new features of ColdSpring 2.0, both complete and envisioned, including functionality such as extensible schema support, events for bean life-cycles, enhanced AOP support, annotation support, and much more.
More details and rsvp can be found
here .
Look forward to seeing you there!
As many people probably know, I'm a huge proponent of Linux operating
systems, most specifically Ubuntu, not only because its open source,
and free, but mostly because I honestly believe it's the best operating
system I've ever come across.
For obvious reasons, I spend a bit
of time trying to convince a variety of people that Adobe should take a
serious look at bringing Adobe products to Linux, the most notable
being ColdFusion Builder.
One argument (and there are a few) against this, I hear over and over is this idea that "Linux Users don't pay for software".
Recently, however, the "
Humble Indie Bundle",
was put up for sale, in which 5 cross platform games (Win, Mac, Linux)
were sold together as a group. The real time data for sales and the
operating system splits has been shared, providing us with a wonderful
aggregate data about the difference between Win, Mac and Linux users, how
much they are willing to pay for software, and if they are willing to
pay at all.
The fun part of this experiment was that the
customer could choose how much they paid for the bundle, which could be
as little, or as much as people liked. It also could be split any way
between the developers, charity, or charity and the developers.
Some interesting stats to note (Taken from the real time stats as of this moment):
1) Current intake across Win, Mac, and Linux - $1,173,536 (which is just cool in and of itself)
2) Windows has the largest market share (no surprise there), with 86670 purchases.
3) Linux is the smallest number of purchases, with 21873 purchases, but
that is only 8153 purchases, less than the Mac platform - 30026
4) The big news here is that Linux people
paid more on average than either Mac, or Windows users.
Win: $8.06,Mac: $10.23, Linux: $14.53
So
much so, that the total income from Linux users, outstrips that of Mac,
even though Mac had more purchases (Mac: $307172.75, Linux: $317846.61)
I
don't think you can ask for a better experiment than this. If Linux
users wouldn't pay for software, then they would be at the
bottom of the list in terms of amount paid. Instead that's where Windows users sit.
Details of the Humble Indie Bundle can be seen here:
http://www.wolfire.com/humble
Their realtime JSON feed can be found here as well:
http://www.wolfire.com/humble/stats
(And they are some cool games as well)
On another note,
Valve have announced that they are releasing their Steam Source Code client to Linux.
This is a huge boon to the Linux community, and while there are no
dates yet, obviously Valve thinks there are people in the Linux
community who will pay for software, otherwise they wouldn't be putting
in the time and effort.
Therefore, you can quite clearly see
that Linux users will, and do pay for software, and in fact put a
higher cost on software than any other OS's user base.
I claim this myth well and truly
busted.
Just a note to let everyone know I set up a twitter account for ColdSpring.
It has automated updates for Git commits, and ticket events. It should be a good resource for helping people keep up to date with the going-on's of ColdSpring, and especially ColdSpring 2.0.
Follow coldspring_fw on Twitter!
Up until recently, the goings-on of what has been happening in ColdSpring 2.0 has been fairly limited to my
twitter account.
At
cf.Objective(), I did a presentation entitled
Dependency Injection Redefined - ColdSpring 2.0 Narwhal where I outlined what work had been done on ColdSpring 2.0, what was planned for the future, and where you could go for more information. (I'll also be doing this presentation at
WebDU in a about a weeks as well).
The first thing to note, is that the code name for this project is
Narwhal.
Why Narwhal? Basically because they are awesome. They roam the ocean
with a huge spike attached to their head, which can be used to impale,
seals, penguins, and apparently also koalas. See - awesome. (Who really has reasons for code names?)
The next most interesting thing to note, is that much of ColdSpring's infrastructure is now hosted on Sourceforge, as it provides a large feature set for us to leverage.
Here you can find the project page, which gives you access to the Git repository that contains the code for ColdSpring 2.0. Sourceforge also provides hosting for the Trac install that is being used to host
our documentation and
tickets and milestones. We are currently investigating options for integration with the current ColdSpring website.
I'm
not going to go into new features in Narwhal, of which there are more
than a few (I have to have some incentive for people to come to my my
WebDu Talk!), but documentation has started on the Wiki, and will be
expanding quickly in the future (If anyone wants to help with that, the
more the merrier!).
You may also note that much of the
documentation is also being generated automatically, in an attempt to
alleviate some of the burden of authoring. Now that there are XMLschemas being used, HTML documentation is being generated from them, and ColdDoc is also being used to generate API documentation for the underlying architecture.
It's still very early days for ColdSpring
2.0, but a Alpha1 does loom on the horizon. That being said, the Git
repository is public, so feel free to pull it down, have a look at what
is happening in the Unit Tests, what documentation is available, and
feel free to discuss it on the
mailing lists. Just beware - this is still pre-Alpha,
so the sand may shift under your feet as new code gets produced, bugs
get quashed and new features get developed. You have been warned!
More details coming soon!
I'm back home again from what I would like to say was, one of the
best cf.Objective()'s
I have ever been to, including the fact that I was incredibly ill for
the last day of the conference. I only managed to catch two sessions in
the afternoon, and otherwise was holed up in my hotel room either fast
asleep, or feeling particularly sorry for myself.
There are many
things I would like to prescribe the incredibly high quality of the
conference to, but the the high calibre of the session presentations
really stands out at the main contributor. I believe that when the
session quality of a conference is so high, the attendees are excited
and motivated by new ideas, which directly translates into fantastic
and thought inspiring hallway conversations, and everything continues
to flow from there.
To make a few particular references to presentations I particularly enjoyed -
Easy and Flexible Validations for Objects - Bob Silverberg
I had not previously taken the time to look at
Bob's
ValidateThis
framework, but let me just say this: It is slick! I was very impressed
by what was possible with very little configuration, and the the
flexibility that was available. My hat is off to Bob for his work with
this validation framework.
Continuous Integration with MXUnit, Ant and Hudson - Marc Esher
Marc
is always a very entertaining presenter, and this presentation was no
exception. I've used Hudson with MXUnit before, but it was still
fantastic to see Marc explain this technology in a very clear and easy
to understand manner. He also gets points in my book for using the
Hudson
Chuck Norris plugin.
Polyglot Programming - Barney Boisvert
If anyone has read
Barney's blog,
they should already know that he's a super smart fellow. This
presentation only solidified this fact, in which Barney dug deep into
the very compelling reasons a developer should potentially look into
developing with multiple languages at a time (something as web
developers we are doing already). Barney covered some really
interesting points about language design, the intent of programming
code and qualifying return on investment in regards to polyglot
programming. All great stuff!
Running ColdFusion on the Amazon EC2 Cloud - Chris Peterson
This
was a session I really wanted to attend, but unfortunately I slept
through due to illness. I wanted to make note of it, as I was told
that
Chris had a completely filled room, and I've been told it was a really interesting presentation all around.
Building Advanced Workflows with ColdSpring - Dan Skaggs
I don't believe I have had the opportunity to see
Dan
speak before this, but this presentation was an eye opener! I came to
this presentation with my 'ColdSpring Lead Developer' hat on to see
what interesting things people were doing with ColdSpring, and came
away with a new found respect for the framework itself, and the
ingenuity that Dan was displaying in his use of it. Dan walked us
through how his company was using ColdSpring to dynamically configure
application workflow and processing - which enables it to be both
incredibly flexible in its implementation and amazingly easy to
maintain.
Bob Silverberg and I also did a two day
Hands on ColdFusion 9 ORM Training
before the conference. Thanks to all our students who attended - we
had a great time teaching the program, and we hope you guys got a lot
out of it. If anyone else is ever interested in taking the training
course, please provide your contact details on the website so we can
contact you at a later date.
All in all, a great conference all around. See you all again next year!
Like
Tyra Banks giving away Vaseline to her entire audience, the attendees of
Bob Silverberg's and I's
ColdFusion 9 ORM Training, that we are running at
cf.Objective(), ever student who attends our training will also get a copy of:
Java Persistence with Hibernate!
You get a book! You get a book! You get a book! Yoouuuuuu get a Booooooooook!!!!
While this is a Java book, and hey, we all write ColdFusion, it is one of
the
books to read on utilising Hibernate within your applications. Reading
this book will give you a huge insight into what ColdFusion is doing
under the covers, which will help out a great deal when building
applications with ColdFusion 9.
So not only do you get some great training from Bob and I, you also get a fantastic reference to take home with you as well!
If that doesn't push you over the edge to
come join us, I don't know what will.
(Ordering now does not provide you with a free set of steak knives, but it will make Bob and I very happy)
A few people have asked me about the ColdFusion Builder scripts for Linux for the new 1.0.
I'm sorry to say that I won't be doing a script for ColdFusion Builder on Linux for the 1.0 release.
I tried this morning to run my usual scripts to get a plugin install out of ColdFusion builder, which ran fine, but after installing and attempting to run it, I was met with a dreaded:
!ENTRY com.adobe.ide.editor.cfml 4 0 2010-03-23 09:39:50.110
!MESSAGE
Whenever I opened a CFML file (which doesn't tell me much about what is going on where).
There is obviously something native built into ColdFusion Builder 1.0, that wasn't present in Beta 3 (I can see some .dll's etc), which is causing this error to occur. Adobe have also done the right thing, and removed all the debugging so that would be hackers have a hard time working out what is going on where.
If you want to get involved in evangelising ColdFusion Builder on Linux, there are some things you can do:
- Vote for the bug!
- Vote for the Flash Builder bug too (if they do one, they will have to do the other!)
- Bug your evangelists! (But do it in a nice way) Conference season is coming up, so have a chat with them when you see them around, and let them know that you would buy a copy for Linux.
- Run it in VMWare / VirtualBox
- Change your OS of choice (ECH!)
At the end of the day, I'm only upset about there being no Linux version, because I think ColdFusion Builder is a great product, and I want it to have as much exposure as it can. We need to convince Adobe that the Linux market is a viable one, and will have a good return on investment.
If I hear of any changes in the Linux landscape for ColdFusion Builder, I'll be sure to let you know.
I believe it is time for some shameless self promotion!
If you were looking for more information on the
ColdFusion 9 Object Relational Mapping Training that the venerable
Bob Silverberg and I are putting on at
cf.Objective() next April, we have just fleshed out the content section of our site to include a
detailed outline of the topics that we will be covering during the training program.
You'll know Bob from his blogging at
http://www.silverwareconsulting.com/, on a variety of all too clever stuff, including CF9 ORM, Git, Unit Testing, and
ValidateThis - his own open source validation framework. He's also an all-around nice guy
You'll
see that we cover a variety of topics with ColdFusion 9 ORM that will
allow you to hit the ground running once you have completed our
training, and it will be taught in a clear, step by step manner that
will leave you wondering why you ever thought the term "Object
Relational Mapper" sounded so scary and complicated.
So if you
are keen on expanding your skill-set with ColdFusion 9, and
specifically with its integrated ORM, we think this is the course for
you, and we hope to see you at cf.Objective()!
cf.Objective() Preconference Classes
When
ColdDoc was first born, the original idea was to just output a port of
Javadoc. Looking into it further, I realised that the mechanics where there to generate
any sort of documentation, not just a static HTML version of the API.
To
that effect, ColdDoc has been refactored so that it is possible to
generate documentation based on an arbitrary Template Strategy.
ColdDoc now ships with both a strategy that generates the port of
JavaDoc and a strategy that generates UML via the
UML2 Tools plugin in Eclipse, and plans are in the works to generate a PDF document strategy as well.
I won't take you through implementation details (there is new
documentation for that), but you can still generate the static HTML API documentation based on JavaDoc, for which you can see an example
here, but now it is done through the
HTMLAPIStrategy.
This also includes support for new ColdDoc based annotations, in which,
for example, you can specify generic types for return/arguments types
like array, struct etc, so you can finally answer that question in your
documentation of "Yes, but what does that array
contain?".
When
working on the ColdSpring 2.0 rewrite (wow, that is long overdue for a
blog post), I also really needed a way to generate UML diagrams from
the code I was writing, as a basic attempt to be able to roundtrip from
UML->CFC and then back again. Also included within ColdDoc is a
strategy to generate the XML that the Eclipse UML2 Tools Plugin uses to
create UML diagrams from. You can see a screenshot of a Class Diagram
here that has been created with this combination of tools.
This
is very useful, as it becomes very useful for collaboration and
software design sessions, even when you have started with UML diagrams,
as software designs can shift during implementation phases, and
diagrams and documentation often fall by the wayside during these rapid
change cycles.
If you are interested in more, or possibly
developing your own documentation generation strategies, download a
copy of ColdDoc, read the new
documentation, and join the conversation on the
google group.
Happy automation of documentation!
In one of my favourite lines from the
Princess Bride -
[Vizzini has just cut the rope The Dread Pirate Roberts is climbing up]
Vizzini: HE DIDN'T FALL? INCONCEIVABLE.
Inigo Montoya: You keep using that word. I do not think it means what you think it means.
..and when using Transaction support with ColdFusion 9 ORM and Hibernate Sessions...It did not do what
I thought it did. It did something else.
To give some back story: As we have
discussed previously, a Hibernate Session is meant to flush at the end of a Hibernate marked Transaction.
So if we are talking to Hibernate natively (for example set up with JavaLoader), and had the following code:
mySession = sessionFactory.openSession();
foo = mySession.get("Foo", 1);
foo.setBar("fooBar");
trans = mySession.beginTransaction();
mySession.save(foo);
trans.commit();
mySession.close();
Which then translates to the following:
- Start a Hibernate Session
- Retrieve Entity 'Foo', with a key of 1, and assign it to a variable foo
- set the property 'Bar' on foo
- Start a Hibernate Transaction, which is tied to the Hibernate Session
- Tell the Hibernate Session to save() our Entity foo
- Commit our Hibernate Transaction, which will flush the Hibernate Session
- Close the Hibernate Session
If we attempt to do the same thing in ColdFusion, it may look something like this:
foo = EntityLoad("Foo", 1, true);
foo.setBar("fooBar");
transation {
EntitySave(foo);
}
Which you would think would work exactly the same as above. Except or one small thing - It Doesn't.
If you read the
ColdFusion Documentation on ORM Transactions, it states:
When <cftransaction> begins, any existing
ORM session is flushed and closed, and a new ORM session is created.
The <cftransaction> can be committed or rolled
back using appropriate ColdFusion tags in <cftransaction>.
When the transaction ends and has not been committed or rolled back
explicitly, it is automatically committed and the ORM session is closed.
If there is any error inside the transaction, without any exception handling,
the transaction is rolled back.
This means that the above ColdFusion code, will actually perform like so:
- Start a Hibernate Session with the ColdFusion Request
- Retrieve Entity 'Foo', with a key of 1, and assign it to a variable foo
- set the property 'Bar' on foo
- Hit the start of the Transaction, flush the current Hibernate Session, close it, and then start another one. At this point, foo has been persisted to the database, and is now detached (and remember detached is generally bad)
- Call EntitySave() on the now detached object foo
- Commit the Hibernate Transaction, and flush the Hibernate Session
- Flush the Hibernate Session, when the ColdFusion Request Ends
Or if you want to look at it another way, it would be the equivalent of doing the following native communication with Hibernate:
mySession = sessionFactory.openSession();
foo = mySession.get("Foo", 1);
foo.setBar("fooBar");
//foo actually gets persisted to the DB at this point
mySession.flush();
mySession.close();
mySession = sessionFactory.openSession();
trans = mySession.beginTransaction();
//foo is now detached
mySession.save(foo);
trans.commit();
mySession.flush();
mySession.close();
The big question at this point, is of course -
Well, why does ColdFusion do it this way then?
There is actually a very good reason, although that reason (imho) is based on a few assumptions.
The first assumption, is that you are going to write your ORM interactions inside your transaction block, like so:
transation {
foo = EntityLoad("Foo", 1, true);
foo.setBar("fooBar");
EntitySave(foo);
}
The second assumption is that, as a developer you will leave the
ORM setting flushatrequestend set to its default of
true, and therefore a split between the Hibernate Sessions is required.
For example, if we had the following code:
transation {
foo = EntityLoad("Foo", 1, true);
foo.setBar("fooBar");
myMethodHereThrowsAnException();
EntitySave(foo);
}
If
we didn't have separate Hibernate Sessions, or at least clearing of the
Session after Hibernate Transaction rollback, , the following would
occur:
- Start a Hibernate Session with the ColdFusion Request
- Start the Hibernate Transaction
- Retrieve Entity 'Foo', with a key of 1, and assign it to a variable foo
- set the property 'Bar' on foo
- Throw an exception, which causes the Transaction to rollback
- The request ends, the Hibernate Session is flushed, and foo gets saved back to the database, since it is still available in the Hibernate Session.
Which is very much against the nature of a rolled-back transaction.
Instead, what actually happens is:
- Start a Hibernate Session with the ColdFusion Request
- Start the Hibernate Transaction, which closed the above Session, and starts a new one
- Retrieve Entity 'Foo', with a key of 1, and assign it to a variable foo
- set the property 'Bar' on foo
- Throw an exception, which causes the Transaction to rollback
- The Transaction end closes the current Hibernate Session, and creates a new one
- The request ends, the Hibernate Session is flushed, and nothing gets saved to the database, as there is nothing in the current Hibernate Session
Given the above assumptions, this is much better, since we want to have the Transaction actually rollback.
That
all being said, what happens to those of us that want to work, or have
an architecture that inclined towards our previous example:
foo = EntityLoad("Foo", 1, true);
foo.setBar("fooBar");
transation {
EntitySave(foo);
}
I.e.
We want to have our Transaction interact with the current Hibernate
Transaction, and not flush at the end of the ColdFusion request, which,
I find to be the solution I prefer for when building applications with
Hibernate.
What can we do then?
There are two things that need to be done, if we want to do this kinda of approach. The first is, to set the ORM setting
flushatrequestend
to false. This means that the Hibernate session won't flush at the end
of the ColdFusion request, and we have complete control over when it
does flush.
To make sure the Transaction interacts with the current request, we can't use <cftransaction> or a transaction {} block, we have to natively interact with the Hibernate Session directly, which is quite straight forward.
foo = EntityLoad("Foo", 1, true);
foo.setBar("fooBar");
trans = getORMSession().beginTransaction();
try
{
EntitySave(foo);
trans.commit();
}
catch(Any exc)
{
trans.rollback();
rethrow;
}
We have to do the extra work to manage the
commit and/or
rollback of the Transaction manually, but this something that could be implemented with a UDF or using Aspect Oriented Programming for reuse.
Now I can use Transactions
to demarcate when I want the Hibernate Session to be flushed, and I'm
not tied to wrapping my entire Entity operations inside a <cftransaction> block.
This is something I really like about how Adobe implemented the ORM integration. If I don't particularly
like the way an individual aspect has been implemented, I have direct
access to the underlying engine, and can therefore interact with it in
whatever way that suits the way I develop and thearchitecture of my application.
Now Transactions mean what I think they mean.
UPDATE 28/10/01:
I just wanted to add a small conclusion to this post, that sums what I am trying to get across simply.
Basically what this all boils down to is: If you are using <cftransaction> or a transaction{} block, make sure you are wrapping them around the entire operation, not just the EntitySave() portion at the end, as otherwise bad things can happen.
If you want to just wrap transactions around the EntitySave() part of your code, you will need to use native Hibernate Transactions like I have done above.
Hopefully that boils the large post above into a nice tasty nugget of knowledge that is far easier to swallow.
I'm a little behind the times on this blog post - but I am presenting and also teaching a course at
cf.Objective() this year.
I will be presenting 2 sessions at the conference:
Dependency Injection Redefined - ColdSpring 2.0: Narwhal
ColdSpring
2.0, codenamed 'Narwhal', is a project that has been going on a little
'behind the scenes', except for the occasional tweet from either
myself, or
Chris Scott.
In
this presentation, we'll look at some of the motivations behind the
complete rewrite of ColdSpring for the 2.0 version, and some of the new
features that will be available that should make dependency injection
easier, and way more flexible than ever before.
I've had in my
to-do list a reminder to write a long blog post on what is going on
with Narwhal, which I should write at some point soon, so you have a
good idea what to look out for. That being said, Narwhal is taking
shape nicely, and I think is going to be a very powerful addition to
the ColdFusion framework landscape.
Advanced Java & ColdFusion Integration with JavaLoader 1.0
This
talk will focus on the new features of JavaLoader 1.0, and how you can
use them to integrate Java with your ColdFusion platform in some new
and exciting ways. If you are interested in JavaLoader 1.0, check out
this
previous blog post, it gives you a good run down on the new features it brings to the table.
We
will also be investigating some of more common 'gotchas' with Java and
ColdFusion integration, especially around ClassLoader issues, which
should be useful for all involved.
Bob Silverberg and I are also going to be a teaching a 2 day training course, just before cf.Objective():
This
will be as very hands on session, where Bob and I will be going through
building an application using ColdFuson 9's Object Relational Mapper
from beginning to end. This will include many best practices,
discussions as well as theory about how the underlying Hibernate engine
works with ColdFusion. No ORM/Hibernate experience necessary.
More detail on the course can be found on
our website, and also on the
preconference page, where you can also check out all the other fantastic training content.
Don't
forget that the early bird pricing for both conference tickets, and
training finishes on the 29th of January, which is coming up soon!
cf.Objective() is looking to be a great conference. Hope to see you there!
This release of
JavaLoader 1.0 includes the following fixes and enhancements:
- Performance improvements in loading classes
- Fix syntax so that JavaLoader 1.0 can be loaded on ColdFusion 7
- Extend the documentation
If you aren't aware of the new functionality in JavaLoader 1.0, you can have a look at my
previous blog post , the
documentation and/or the
presentation I did at MAX for more information.
Credit and thanks to Fred Roeber for his contributions to the NetworkClassLoader in JavaLoader.
If you are interested in hearing more about ColdFusion and Java integration please come to
cf.Objective(), where I will be presenting on
Advanced Java & ColdFusion with JavaLoader 1.0.
In the mean time, download JavaLoader, and please report any bugs that you find.
ColdFusion Builder 3 is
up on Labs , and available for download, so it's now time to update my scripts for getting it up and running on Linux.
The procedure is as following:
- Download the intermediate build *for MAC* (very important! The Windows Adobe Licensing will prevent you from using the Windows version any more, as it uses .dll files, and those don’t work on Linux, but the Mac version works on Linux!)
- Download coldfusion_builder_b3.zip .
- Unzip coldfusion_builder_b3.zip into a directory
- Unzip coldfusionbuilder_b3_install_mac_121709.zip
- Unzip /cfbuilder_install.app/Contents/Resources/cfbuilder_install.app/Contents/Resources/Java/Disk1/InstData/Resource1.zip (may be easier to do that into another folder)
- From the unzipping of Resource1.zip grab /Z_/dist/_macos_87b1877cbdff_zg_ia_sf.jar and put it in the same folder as where you unzipped coldfusion_builder_b3.zip
- Run ’./Rip.py’ from the folder you unzipped it to.
- You should now have a ’plugins’ and ’features’ folder with just the bits you need to install into Eclipse.
- Enjoy CF Builder in Linux :D
Enjoy!
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.
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!
JavaLoader 1.0 is moving from Alpha to Beta with only a few small bug fixes and enhancements.
If you are not familiar with JavaLoader 1.0's functionality, you can have a look at my previous blog post , the documentation and/or the presentation I did at MAX for more information.
The was a logic bug in the way that dynamic compilation was occuring across multiple source directories that has now been resolved, and should also ensure that compilation is much faster than it was in the Alpha.
The other enhancement was to allow for relative paths in the Spring integration. To explain, in the Alpha, you would often end up having to input your beans similar to this:
<coldfusion:cfc id="message"
script-source="file://${root.path}/model/Message.cfc"
script-interfaces="com.IMessage"
/>
Which can be a bit of a pain, as injecting a dynamic value for ${root.path} is not as easy in Spring as it is in ColdSpring.
Now, by default, 'script-source' attributes are relative to the calling page, so you can do your bean definitions like this:
<coldfusion:cfc id="message"
script-source="file:///model/Message.cfc"
script-interfaces="com.IMessage"
/>
and the path will be worked out for /model/Message.cfc relative to your application path.
Oh, and don't forget - if you are into integrating Java and ColdFusion development, make sure you sign up for the Google Group !
JavaLoader 1.0 Beta can be downloaded here .
I had the wonderful opportunity to present and attend MAX for the first time ever this year, and I can honestly say, it was a fantastic time all around.
MAX is completely unlike any other conference I've ever attended, both in terms of its sheer size, and also in terms of the wide gamut of Adobe community members that are in attendance. I managed to meet a variety of people that I would never have talk to in my regular travels both in term of physical locations and also technological disciplines.
I also had the pleasure of presenting the last session on the last day, so I was very happy when I had a fairly reasonable turn out for my
ColdFusion for Java Developers presentation. The presentation took the perspective that the attendees were/are already doing Java development, but want to port those tools and codebases that they are already using to a ColdFusion environment, so they could take advantage of ColdFusion services and language. From there we look at the variety of techniques that you can use to enable ColdFusion->Java communication, as well as Java->ColdFusion. This includes a aan overview of a lot of the JavaLoader 1.0 feature set in the process.
If you are one of these people, and/or you are doing development in both ColdFusion and Java, you can watch the
recording of my presentation on Adobe TV . You may want to skip the first 5 minutes, as for some reason the recording has me doing all my set-up before I start speaking.
For those of you who have been running ColdFusion Builder on Linux, you may have noticed that the installer structure has changed since the last public beta, which means that my
previous instructions will no longer work.
This means we have to jump through some different hoops to get things to work this time around.
The steps are now the following:
- Download the Windows version of ColdFusion Builder
- Download my cfbuilder_builder.zip file and unzip it somewhere
- Unzip the windows installer like so: 'unzip cfbuilder_windows_public_beta2.exe'
- Under InstallerData/Disk1/InstData/ you will find Resource.zip
- Unzip Resources.zip
- Under Resource1/Z_/dist/ you will find _windows_fe4d6d02bcbb_zg_ia_sf.jar
- Copy _windows_fe4d6d02bcbb_zg_ia_sf.jar to the same directory you unzipped cfbuilder_builder.zip to (and should now have Rip.py and plugins.txt in it)
- Run 'python Rip.py', and wait.
- You should now have 2 folders in that directory 'plugins' and 'features', which you can now copy into your eclipse directory to do a plugin install.
As new versions of ColdFusion Builder become public, I will attempt to keep this script up to date.
In case you missed the tweets, I am very pleased to announce that
Ben Forta will be joining us in Melbourne, Australia for
cf.Objective(ANZ) !
Ben will be joining ColdFusion Evangelist
Terry Ryan to represent Adobe ColdFusion here in Australia, and the two of them will be around to chat to all attendees about ColdFusion and the Adobe ecosystem.
Don't forget that the early bird price comes to a close on the 12th of October, which is fast approaching, so please make sure you get yourself registered so you can take advantage of it.
See you all when I get back from MAX.
At a very ANZ friendly time of Wednesday, 10am Melbourne time, I'm going to be joining super-smart guys
Brian Kotek and
Barney Boisvert on CFPanel to talk about Java Integration with ColdFusion in the CFPanel Connect Room.
This
is obviously a topic that is quite near and dear to my heart, but it is
also one that I think often doesn't get its 'surface scratched' (so to
speak). In my experience I often see the information presented is more
on the 'how to get set up' side, and less on the deeper details on what
can actually be done.
I'm really looking forward to getting a chance to share experience with the other CFPanel
members, and really get deeper into some interesting Java and
ColdFusion integration, in regards to how people are using it, what
benefits they are reaping, and also what problems they have encountered.
For more details, and and all the other time zones, check out the
CFPanel post.
Hope to see you in the Connect room!
It's been a bit of a bumpy road getting here, but registration for
cf.Objective(ANZ), being held in Melbourne Australia on the 12th and 13th of November is finally open!
The
program is full with a stack of really interesting presentations, ranging from
JVM Tuning and Optimisation, to
ModelGlue 3: Gesture, all the way to
Connecting hardware up to ColdFusion!
We have an international cast of speakers, including notable luminaries such as
Dan Wilson and
Mike Brunt, as well as many talented speakers from the Australia and New Zealand Regions, including
Geoff Bowers,
Robin Hilliard,
Kai Koenig and many,
many more.
Helping
organising a conference over international borders has been quite the
learning experience, however I am very proud to be part of the steering
committee that is bringing cf.Objective(ANZ) to the Pacific Region.
If
you are looking to learn about enterprise development, and hang out and
talk to a slew of like minded developers for two days,
register for the conference, and come on down! It's going to be a great time!
JavaLoader
1.0 is a project I've been talking about off and on for the past 6
months or so, and I'm very happy to announce that a release version is
now complete.
There are several key new features to JavaLoader 1.0,
Dynamic Compilation
Now
if you are working with JavaLoader, you don't have to bundle your Java
code into a jar to load. You can simply specify to JavaLoader which
directories are your source directories, and JavaLoader will compile
and load them on the fly for you! JavaLoader will also check to see if
your source code has changed, and re-compile it as necessary!
For
example, if I wanted to compile and load a HelloWorld.java (that has a
simple 'Hello World' method) in my ./src directory, I could do:
javaloader = createObject("component", "javaloader.JavaLoader").init(sourceDirectories=[expandPath('./src')]);
And then create my HelloWord object as per normal:
<cfset helloWorld = javaloader.create("HelloWorld").init()>
<cfoutput>#helloWorld.hello()#</cfoutput>
No more ANT tasks, no more export to .jars, JavaLoader handles the compilation and deployment of your Java code for you.
ColdFusion Component Dynamic Proxy
For those of you who are not aware of what a
Dynamic Proxy
in Java is, it is essentially an Object that can mimic an object that
implements a given set of Interfaces. The method invocations against
that object are intercepted, and you can essentially do whatever you
like with them.
In JavaLoader 1.0, I've written a Dynamic Proxy that you are able to wrap around a CFC, and thereby make Java Objects
think
they are interacting with a native Java object, but are in fact,
talking to your CFC. When a Java Object calls a method on the Dynamic
Proxy, it is passed through and invoke directly on the CFC transparent
to your Java layer.
For example, the
java.lang.Thread Java object can take an instance of the interface
java.lang.Runnable in its constructor argument. Using JavaLoader's Dynamic Proxy, we can pass it what it
thinks is an instance of the Runnable Interface, but is in actuality a ColdFusion Component like so:
//give us access to the CFCDynamicProxy
CFCDynamicProxy = javaloaderloader.create("com.compoundtheory.coldfusion.cfc.CFCDynamicProxy");
//create my Runner CFC
myRunner = createObject("component", "MyRunner").init();
//wrap it in a Dynamic Proxy, that implements Runnable
runnerProxy = CFCDynamicProxy.createInstance(myRunner, ["java.lang.Runnable"]);
//pass it to the Thread and call run()
thread = createObject("java", "java.lang.Thread").init(runnerProxy);
thread.run();
...and suddenly we have true seamless communication from Java to ColdFusion components.
This
is actually something I wish I had written three years ago. It would
have radically altered how I would have written a lot of software.
Spring Integration
A natural progression from the Dynamic Proxy, was to implement a
Custom Schema in
Spring, so that you could instantiate and dependency inject your ColdFusion components with your Java Objects inside Spring itself.
Initialising
Spring with JavaLoader is covered in the documentation, but to give you
a quick taste, once we have Spring running, we can include a CFC in our
spring.xml with:
<coldfusion:cfc id="message"
script-source="file://home/www/model/Message.cfc"
script-interfaces="com.IMessage"
/>
Where
script-source is the absolute path to the CFC you want to instantiate, and
script-interfaces are the Java interfaces that your dynamic proxy will implement, and your CFC has mirrored inside it.
This
Component then gets treated like a normal Spring bean, so it can be
autowired, injected, aop'd and all the usual functionality that a
Spring bean has access to.
There are some downsides to implementing Components inside Spring, so be sure to check out the documentation for more details.
Read More
For more details, have a read of the
extended documentation, and have a look at the examples provided in the
download.
Since
this has become a far larger project than its original inception, I've
done away with the Riaforge Forum, and have started a new
javaloader-dev google group for support and discussion of JavaLoader and Java and ColdFusion integration.
While
I'm very confident in the code that I have written for JavaLoader 1.0,
I decided to call it an Alpha, simply because it has only really been
tested by me and my Unit Tests. Knowing from experience that users of
Open Source Software have a tendency to use and abuse libraries far
above and beyond what the author had ever originally dreamed led me to
decide to call it Alpha until the community has really had a good
change to play with it.
Please
download JavaLoader 1.0, take it for a spin, sign up for the
mailing list, and if you find any problems please do let me know.
A question I've seen show up quite often in Hibernate forums is 'How do
I get Hibernate to generate UTF-8 Compliant Tables and/or columns when
it generates my database tables when using mySQL?'
It's
actually, quite easy to do, but often (myself included), people end up
looking deep into Hibernate for some mysterious setting.
In
reality, it's just a question of setting up your database correctly.
The first thing you need to do is set your database to use UTF-8
character coding as its default, like so, when creating it:
create database my_new_database default CHARACTER SET = utf8 default COLLATE = utf8_general_ci;
This will ensure all tables that are created by ColdFusion's ORM are created with a UTF-8 character set.
That being said, I have seen issues when data is being sent up to MySQL in UTF-8 form getting corrupted. To ensure the database drivers use the correct character set as well we add useUnicode=true&characterEncoding=UTF-8 to our MySQL connection string in the ColdFusion Admin under the datasource's Advanced Settings.
There you go! UTF-8 support in mySQL with ColdFusion ORM!
This is something I just ran into with ColdFusion 9's ORM implementation, that thankfully Rupesh got back to me very fast on, otherwise, I probably would have been confused for quite a while.
I
needed to overwrite a get and set value in a CFC I was creating. We
will say for arguments sake I was hashing a password on a User object
(even though I was doing something completely different), like so:
component output="false" persistent="true"
{
property name="userid" fieldtype="id" ormtype="int" generator="identity" hint="primary key";
property name="name" type="string" notnull="true" length="200" hint="The user's name";
property name="password" type="string" length="200" hint="The user's password";
public void function setPassword(String password)
{
instance.password = hash(arguments.password);
}
public String function getPassword()
{
return instance.password;
}
}
This is how I would usually have written my Components, storing my data in variables.instance, so they wouldn't overlap with any of the data stored in my variables scope.
What I couldn't understand was, my password would keep getting stored in my database as NULL, even though I had explicitly set it.
Thankfully Rupesh explained that when the ColdFusion ORM retrieves and populates data in an ORM managed CFC, it directly accesses the variables scope in the CFC. So in the above instance, the ORM was finding no data for the password, and interpreting that to mean that I wanted to insert that as NULL into my database.
Therefore, to get my code to work was to store my data in the variables scope, like so:
component output="false" persistent="true"
{
property name="userid" fieldtype="id" ormtype="int" generator="identity" hint="primary key";
property name="name" type="string" notnull="true" length="200" hint="The user's name";
property name="password" type="string" length="200" hint="The user's password";
public void function setPassword(String password)
{
variables.password = hash(arguments.password);
}
public String function getPassword()
{
return variables.password;
}
}
This way the ORM can find exactly what it is looking for, and I can manipulate my data the way I want.
The fact that the ORM integration works this way is actually a very good thing, as it means that my password above doesn't get hash'd
over and over again, but it is something we all should be aware of when
you want to overwrite the implicit get and set functions that get
generated by <cfproperty> declarations.
In the previous two articles, we started the discussion of Hibernate Sessions and how they related to the ColdFusion 9 ORM. We noted the fact that a Hibernate Session
keeps track of what objects it loads, and can therefore update it when
it realises that the object has changed. The question then posses
itself - if a Hibernate Session has closed, which happens at the end of every ColdFusion request, what happens to the objects that it was managing?
Going forward, I'm going to reuse the Musician model I used in the Introducing ORM in Adobe ColdFusion 9 beta Adobe DevNet article, for which the updated code samples can be found here.
The very short version for those who don't wish to look at the code, is
a model in which we have a Musician, who has a name, and an age, and
he/she also has a many-to-many relationship to an array of Instruments,
which they can play.
As far as Hibernate is concerned, there are three different states for any Object that it interacts with.
Transient
An Object/Entity (you could use either word) is Transient, if it has just been created with a
createObject() or
new operator, but has yet to be saved with the
EntitySave() method.
Obviously, if transient objects are not persisted via EntitySave(),
then they never get stored in the database, and will eventually be
garbage collected.
For example, our musician
john below is a transient object:
<cfscript>
import com.*;
>//this is transient
john = new Musician("Transient John");
</cfscript>
Since john is never saved, it will always be transient.
Persistent
A
Persistent object is one that has either come from, or has been saved in the database, within the scope of the current
Hibernate Session (which is an important point to note).
For example, we can make our musician
john below persistent, by:
<cfscript>
import com.*;
//join is transient
john = new Musician("Transient John");
john.setAge(47);
//john is now persistent
EntitySave(john);
</cfscript>
Therefore, since
john was inserted into the database, he is
persistet.
By the same token, if we retrieve an object through the ORM, it is also
persistent:
<cfscript>
>//larry's ID is 2
//larry is also persistent
larry = EntityLoad("Musician", 2, true);
//As larry is persistent, we can change his age, which will be reflected in the database
larry.setAge(19);
</cfscript>
Since larry was retrieved from the database, he is also persistent.
Detached
Detached objects are ones that used to be
persistent, but the
Hibernate Session has
closed. Detached objects were one of the biggest things for me to wrap
my head around when starting with Hibernate, and can cause a few really
tricky errors to occur.
To explain
detached a little better, we can go through a
scenario that will causes them, as I think this is something that a lot
of ColdFusion developers are going to end up running in to.
Let's have a new musician, called
Tim, and time is going to have 2 instruments that he plays, the Flute and Piano.
We are going to grab Tim out of the database, and then put him in our
ColdFusion Session scope, so we can keep track of him from request to request.
<cfscript>
tim = EntityLoad("Musician", 2, true);
session.musician = tim;
</cfscript>
For this request, Tim will be
persistent, however, on the next request, Tim is now
detached, as the
Hibernate Session will have closed at the end of the previous request.
Therefore, if I do this:
<cfscript>
instruments = session.musician.getInstruments();
</cfscript>
<cfdump var="#instruments#">
You will see the following (not well written) error:
failed to lazily initialize a collection of role: Musician.instruments, no session or session was closed
If you are running ColdFusion via the console, you will also see something similar to:
org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role: Musician.instruments, no session or session was
closed
What does this error even mean?
First of all, we should remember that the collection of Instruments our Musician can play are
lazy loaded. This means they aren't actually loaded from the database into the object until you actually request them.
The important thing to note here, is that lazy loading with Hibernate can only occur if the
Hibernate Session that the object was originally loaded from is still open. To put it another way, if the
Hibernate Session has closed, then Hibernate has no record of what the object was doing, and therefore can't do any lazy loading.
In the above example, since the previous ColdFusion request ended, the
Hibernate Session has closed, and if you attempt to access a relationship that is set to lazy load, and has yet to be loaded, this error occurs.
Its should be noted that singular properties are not lazy loaded -
therefore, if you are just grabbing a property on an object that is
detached, no error will be thrown.
What can we do to mitigate this issue? There are a few options, one of
which is, to simply retrieve the object all over again, like so:
<cfscript>
session.musician = EntityLoad("Musician", session.musician.getMusicianID(), true);
instruments = session.musician.getInstruments();
</cfscript>
<cfdump var="#instruments#">
Or, like so:
<cfscript>
session.musician = entityLoadByExample(session.musician, true);
instruments = session.musician.getInstruments();
</cfscript>
<cfdump var="#instruments#">
As then the object we are dealing with is considered
persistent, as it is attached to the currently open
Hibernate Session. This tends to be the easiest of the options when dealing with detached objects.
Another interesting way of making our
detached object
persistent is like so:
<cfscript>
EntitySave(session.musician);
instruments = session.musician.getInstruments();
</cfscript>
<cfdump var="#instruments#">
This may look strange, but what is actually happening when you call EntitySave(), is that it calls the Hibernate method
saveOrUpdate().
A quick version of what saveOrUpdate() does is -
- If the object being passed in is transient, INSERT it to the database.
- If the object is already persistent in the current Hibernate Session, do nothing.
- If there is already an object that represents the same Entity data persistent in the Session, throw an exception
- Otherwise, update the database with the information in the passed in object, and set its state to persistent.
Therefore in the example above, the data stored in
session.musician will be saved again to the database, and since
session.musician is now deemed
persistent, we can successfully do lazy loading of the instruments.
However, if a situation like this would occur:
<cfscript>
musicianTwo = EntityLoad("Musician", session.musician.getMusicianID(), true);
EntitySave(session.musician);
</cfscript>
We get the following error:
a different object with the same identifier value was already associated with the session: [Musician#2]
Since Hibernate deems the
persistent object that is currently connected to the
Hibernate Session much more important than the detached object, which makes sense, as the
persistent object has the most up to date version of the data stored in the database.
Generally speaking, if you at all can, try and avoid having
detached
objects in your application, as this is generally a Good Thing™, and
will allow you to avoid some pretty decent headaches as you develop
with CF9 ORM. Strategies such as storing IDs in the
ColdFusion Session
and having an API to return a new Object by that ID on request is often
a good way to go, rather than storing the Entity itself in the
ColdFusion Session. That being said, if your architecture means that you have to have
detached objects this series should be enough to give you a basic grounding the concepts of
object state, and how to manage it.
If you want to learn
more about managing Hibernate Object States (and there is far more to it than I have written here), you can read more about this in the
Hibernate documentation,
but this should hopefully have given you a decent enough overview to
avoid some possible pitfalls when handling persistence with ColdFusion
9's ORM integration.
This year it is going to be my absolute pleasure to be attending and presenting for the first time ever at the
Adobe MAX conference.
My presentation is entitled
ColdFusion For Java Developers and is scheduled for Wednesday, 5:00pm.
Primarily
aimed at Java developers, it is going to cover a myriad of ways that
you can integrate your already existing Java code base into a
ColdFusion application, and also get your Java model talking seamlessly
to your ColdFusion code base.
The official synopsis is:
Ever since ColdFusion was re-implemented as a J2EE application, the
benefits of combining Java and ColdFusion application development have
been easy to see, and widely used across the ColdFusion landscape.
This
talk will look at some of the ways we can already take advantage of
common Java libraries in ColdFusion, and enable Java developers to
leverage the libraries and frameworks that they are already using. We will also be looking at
how Java developers can seamlessly leverage the dynamic scripting
language of ColdFusion, within their Java code, to easily enable them
to take advantage of some of the RAD features that come bundled with
ColdFusion.
That
being said, if you are a ColdFusion developer who likes to also work
with Java, you may wish to come along, as there will be several
techniques demonstrated that will be provided by the forthcoming
JavaLoader 1.0 release that should (hopefully) interest you greatly.
I look forward to seeing you all at MAX, and also at the
ColdFusion Unconference!
My apologies if this is a little belated, but here is an update to the sample code that was posted with my DevNet article Introducing ORM in Adobe ColdFusion 9 beta.
Unfortunately several things changed in CF9 between the time that I wrote the article and when the public beta came out.
The major differences between the sample on Adobe.com and this one are:
- The datatype attribute on cfproperty was changed to ormtype.
- Disable Implicit UDF Registration is now turned on by default, so any implicit setter/getter inside a CFC has to be prefixed by 'this.' for them to function.
I will endeavour to keep this download up to date as new versions of ColdFusion 9 are release.
coldfusion9_orm_sample.zip
Hopefully with my previous article, I've got you understanding how Hibernate Sessions
work behind the scenes of ColdFusion. However, there is one statement
I made that requires further clarification before we go any further.
I wrote:
"This means that changes to your objects will also not be persisted to your database, until the end of your ColdFusion request, as that is when the Hibernate Session is closed."
Technically this isn't exactly true, but it was a concept that confused me a lot
when first starting with Hibernate, so I wanted to get people thinking
in the right direction without complicating things too much.
The Problem
For example, a situation like this made me scratch my head for a long time when I first started working with Hibernate:
We have 5 Musicians in our database, and we want to delete one, and then re-list all of them again.
<cfdump var="#ormExecuteQuery('from Musician')#" label="query">
<p>We are now deleting!</p>
<cfset musician = entityLoad("Musician", 3, true)>
<cfset EntityDelete(musician)>
<cfdump var="#ormExecuteQuery('from Musician')#" label="query">
And we would see a result like:
Tom, Jerry, Ben, Bob, Richard
We are now deleting!
Tom, Jerry, Ben, Bob, Richard
This shows me exactly the same data we before and after my
EntityDelete(). Wait... what? Didn't I delete Musician Number 3?
The fact of the matter is, yes, we deleted Musician #3, except since the
Hibernate Session hasn't been
flushed yet, the data hasn't been persisted to the database. Therefore, when we run out HQL query, we see all the Musicians!
Hibernate Session Flushing
Before we go directly into how to solve this problem, what does the
Hibernate documentation say about when
Session Flushing actually occurs?
This process, called
flush, occurs by default at the following
points:
-
before some query executions
-
from org.hibernate.Transaction.commit()
-
from Session.flush()
So what does that actually mean in terms of ColdFusion?
- Before some query executions - So at some arbitrary points during the Hibernate Session life cycle it may flush. Obviously not something we can rely upon.
- At the end of a Transaction. So if we our code in a <cftransaction> block, it should flush at the end of that transaction, and therefore the changes to our objects will be persisted in the database.
- When we call ORMFlush(), or at the end of a Session when it is called implicitly.
Therefore, if we wrap the relevant code in or Transaction, like so:
<cfdump var="#ormExecuteQuery('from Musician')#" label="query">
<cftransaction>
<p>We are now deleting!</p>
<cfset musician = entityLoad("Musician", 3, true)>
<cfset EntityDelete(musician)>
</cftransaction>
<!--- Session should be flushed now --->
<cfdump var="#ormExecuteQuery('from Musician')#" label="query">
We should get a result of:
Tom, Jerry, Ben, Bob, Richard
We are now deleting!
Tom, Jerry, Bob, Richard
Alternatively, we could also do:
<cfdump var="#ormExecuteQuery('from Musician')#" label="query">
<p>We are now deleting!</p>
<cfset musician = entityLoad("Musician", 3, true)>
<cfset ORMFlush()>
<!--- Session should be flushed now --->
<cfdump var="#ormExecuteQuery('from Musician')#" label="query">
And we would also see the same results.
So as you can see it is not exactly
true that your object data will only get persisted at the end of you
request, as you actually have a lot of control over exactly when the Session gets flushed.
It is important to note that 9/10 times you probably won't care when the Session gets flushed
and the changes to your objects actually get persisted in the
database. However, there will be instances in which you will care, and
your code depends upon having your data persisted in your database. You
now have the tools to manage your persistence life cycle and know what
is going to happen when.
Now that ColdFusion 9 is in public beta, I wanted to write some
articles explaining how Hibernate is actually working under the hood of
the nice ORM integration we now have. Like all powerful tools, they
can do a great many things - but only if you really understand how they
work. Otherwise, they are potential for disaster if you don't know
what you are doing.
Hibernate Sessions
The first thing we need to cover is the concept of
Hibernate Sessions,
which has been nicely hidden away from ColdFusion developers by the
Adobe Engineering team, but is still something that you need to really
understand to be able to leverage Hibernate in CF9 properly. (At least
IMHO).
First things first - don't confuse this with ColdFusion Sessions, or
any sort of browser session. Hibernate is built as a generic solution
to ORM, so it has no specific ties to web state. A
Hibernate Session
is a lightweight object that is used to delineate when Hibernate is
meant to start working, and when it is meant to stop, which is often
referred to as a
unit of work. This may span several transactions and/or INSERT, UPDATE, DELETE and SELECT statements as the Session gets used.
This is very much like the start and stop of a conversation, we say
hello at the beginning, and say goodbye at the end, and whatever we
need to discuss goes on in between, except we just tell the Session to
open() and
close(), rather than "hello" and "goodbye".
Actual Hibernate Java code would look something like this:
//open the session from the SessionFactory (which is where sessions come from)
Session session = sessionFactory.openSession();
//get fred, our caveman
Caveman fred = session.get("Caveman", 2);
//change his age to 32
fred.age(32);
//close the session, which will update fred.
session.close();
I'm not going to get into where the
SessionFactory comes from, as all you really need to know is: it's where
Hibernate Sessions come from.
So what we are doing here is:
- opening a Session, i.e. we tell Hibernate 'Hi', which starts our conversation.
- Tell Hibernate to retrieve our Caveman fred, through our session.
- We change the age of fred to 32.
- We close our session, and say 'GoodBye' to Hibernate for now.
There are several things that happen here that are worth noting at this point, even in this relatively simple piece of code.
The first is, by doing
session.get("Caveman", 2), we have effectively told our
Session to track this object, and make note of any changes that happen to it.
We have also cached a copy of
fred in the Session itself, so that if we were to attempt to retrieve
fred again, we would get the same copy. This is both for performance, and also so that the
Hibernate Session only has to deal with one object that represents the data behind
fred, which will be a fairly important concept as we continue in this article series.
Since our
Session is tracking
fred, any change we make to
fred will be persisted to the database when we close our
Session. In that way, Hibernate is quite transparent in the way it implements persistence.
It should also be noted at this point: Unless you specify otherwise,
Hibernate will only run INSERT, UPDATE and DELETE when the session is
closed, generally as a batch. This is so that, for example, multiple
changes happen to
fred during the length of our
Session,
there only needs to be one UPDATE statement when we are finished. This
is an important thing to note, as it is possible to get confused as to
why your changes to your objects aren't appearing in your database during your
Session, but do appear afterwards.
Hibernate Sessions and Web Applications
In web applications, it is usual to manage
Hibernate Sessions by
opening one up at the beginning of a HTTP request, and then closing at
the end of the request. The length of a single HTTP request is a good
size to carry on a nice conversation with Hibernate.
Strangely enough, this is
exactly what ColdFusion is doing for you behind the scenes. It starts a
Hibernate Session
when your request starts, and ties it to that particular request, and
then closes it at the end of the request. (It is quite likely that the
Session actually only gets created when first requested, but for the
sake of argument it is easier to explain it as it gets opened at the
start of the request)
From there, each of the ORM functions, EntityLoad, EntitySave, EntityDelete,
etc, all interact with that
Session that exists behind the scenes, for that request.
This means that changes to your objects will also not be persisted to your database, until the
end of your ColdFusion request, as that is when the
Hibernate Session is closed. In later articles, I'm going to cover some ways this can cause some trouble, and how to work around them.
You are actually able to flush the Session, which
forces it to persist any changes to the database right then and there,
and not wait until the end of the request, which can sometimes be very
useful.
To do this in Java, you could go:
session.flush();
But in ColdFusion you write:
ORMFlush();
UPDATE: You can read more about Session Flushing here, which outlines that there are times in which Hibernate will flush a Session in the middle of Session, however, there is often no guarantee except in specific circumstances.
It's also interesting to note you have direct access to the Hibernate Session if you so desire through ORMGetSession(), which gives you access to that request's Hibernate Session object, so you can interact with it directly.
There is much more to Hibernate Sessions and how they relate to the
objects that they manage, but this will give us a good start to build
upon for now.
You can read more about ColdFusion and Hibernate Sessions in the documentation Hibernate Session Management.
UPDATE (21 October 2009): This will no longer work with Beta 2 of ColdFusion builder. If you are looking for a more up to date technique for installing ColdFusion Builder on Linux, please see this post .
So I’ve managed to hack my way to getting Bolt installed on Linux, and
it wasn’t quite as painful as I thought it was going to be. It is
essentially a pure plug-in install, but we have to jump though a few
hoops to get the plug-in itself.
I would suggest starting with a clean installation of Eclipse 3.4.2, with a clean workspace, just to make sure there are no major issues.
Here are the steps you need to reproduce:
-
Download ColdFusion Builder for Windows. I used 32 bit, but I don't think it matters.
- in the terminal run: unzip cfbuilder_{your version}.exe, to extract all the content.
(Update 16/09/09, as Universal Extractor isn't actually needed, see comments)
-
Install Universal Extractor - http://legroom.net/software/uniextract
(I installed under CrossOver office, and it worked a treat, should work under Wine)
Extract the data from bolt.exe (I extracted to /tmp/bolt), using Universal Extractor
- Extract /tmp/bolt/InstallerData/Disk1/InstData/Resource1.zip, (I extracted to /tmp/Resource1/)
-
Under /tmp/Resource1/Z_/dist/installer you will find ’installerdist_zg_ia_sf.jar’
- Unzip this .jar file, you will find the /plugins and /features directories you need for a plugin install on Eclipse.
- Extract those 2 directories into your Eclipse installation.
- If you want the CF Builder Extension Samples, you can find them inside /tmp/Resource1/Z_/installer as ’samples_zg_ia_sf.jar’. Open up that archive and there they are!
- Fire up Eclipse, and you should now find you have a ColdFusion perspective!
So far, there is only 1 annoying bug I’ve found.
When using CTRL+J to insert a snippet, it inserts perfectly, however, I
find I have to click outside of Eclipse, and then back into Eclipse
with my mouse before I can edit again (very weird, I know). It's annoying, but I can work around it.
Hopefully we can provide enough free testing for ColdFusion Beta on
Linux, and prove we have a large enough user base, that we can get
supported on Linux.
Make sure any bugs you run into are reported to the
ColdFusion Builder Bug Tracker page, so that Adobe is aware of them.
As I'm sure you have all noticed, ColdFusion 9 Public Beta is now available for download.
I am really excited about this release, and think it is going to change
the way a lot of people are going to be developing applications going
forward.
Some of my favourite new features are:
- Hibernate ORM
As we all well know, I have a special place in
my heart of Object Relational Mapping, and the inclusion of Hibernate
in ColdFusion gives me some lovely goosebumps. I've worked with
Hibernate before, and it really is a top-notch piece of software, and a
great ORM to work with. I can also honestly say that the engineers at
Adobe have done an excellent
job of providing it to ColdFusion developers in a way that (a) is quite
easy for new users to pick up and use and (b) exposes much of the
internals of Hibernate to those who are more experienced, and want to
do some more complicated things.
I wrote an article for Adobe Devnet, called Introducing ORM in Adobe ColdFusion 9 Beta,
which I think is a nice introduction to the basics of ORM, and takes
you through some simple steps of using Hibernate in Java without
getting too complicated.
Over the next few weeks I plan on writing several more blog posts
outlining Hibernate within the ColdFusion context, and giving people
more of an idea of what is going on under the hood so they can avoid
some 'gotchas' I think people will run into when starting to play with
Hibernate.
- Language Enhancements
I am really enjoying writing so much more of my application in
CFScript. Coming originally from a Java background, CFScript is a much
more natural fit for the way I like to write code, so I am very excited
to be able to do so much more of my development using CFScript, rather
than being forced to cut between CFScript and CFTags.
Oh yeah, and let's not forget things like implicit getters and setters, the new and import keywords, and nested transactions (finally!). So many good things!
- ColdFusion Builder
I could go into each and every part of ColdFusion Builder, for
which there is a fair amount, but overall I think this is an amazing
release for what is essentially a 1.0 IDE.
My favourite part of CF Builder is quite likely just the code
assistance in scripts and tags. I have a ridiculous ability to
misspell/miss-type 'arguments' on a regular basis. Now I just type
'arg', hit ctrl+space, and it enters it for me. Aaaah, no more misspellings !
So if you haven't already, grab a copy of
ColdFusion 9 Beta, and
ColdFusion Builder Beta, and enjoy!
I just wanted to make sure that everyone was aware of the Melbourne leg of the ColdFusion 9 and Flex 4 Usergroup Tour, on June 17th.
Adobe ColdFusion 9
The Adobe ColdFusion team is hitting the road to discuss Centaur,
the next version of ColdFusion, and Bolt - code name for the highly
anticipated first ColdFusion IDE from Adobe. Learn about exciting new
features and discover how Centaur and Bolt will accelerate your
ColdFusion application development. Visit the tour calendar
http://bit.ly/UGTour09 to find a stop near you!
Flex 4 / Flash Catalyst
Come learn about the newest features in the Flex 4 framework, Flex
Builder 4 and the newest Adobe product -- Flash Catalyst. See product
feature demos, and learn how the new Flex framework powers both Flash
Catalyst and Flex Builder to speed the development and testing of rich
Internet applications and content.
More details and RSVP can be found here .
Hope to see you there!
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).
Previously I discuss a simple mechanism in
Conduit
to enable you to apply data transformations on outgoing data from
ColdFusion to Flex, using a simple inheritance strategy, to overwrite
how the CFC=>Flex VO object translation occurred.
The only issue there is, if you need to be able to do different
transformations, you end up having to combine separate code-bases into
a single
CFDeserialiser or CFSerialiser , which is not very portable, or decoupled.
To solve this problem, Conduit has
filters that can be
applied at different points in the AMF communication life cycle, and
manipulate the data that is being passed through.
So, if we take the example we looked at previously, where we reversed
every simple value that was travelling from ColdFusion to Flex, we can
convert this into a simple filter, which is much easier to apply, and
can be combined with other filters to do fairly complex data
manipulation.
An example ReverseFilter looks like this:
<cfcomponent hint="filter that reverses all strings" output="false">
<cffunction name="init" hint="Constructor" access="public" returntype="ReverseFilter" output="false">
<cfargument name="args" hint="the argument ConfigMap" type="struct" required="Yes">
<cfscript>
return this;
</cfscript>
</cffunction>
<cffunction name="doFilter" hint="does the filtering - reverses strings" access="public" returntype="void" output="false">
<cfargument name="filterData" hint="the data for the filter" type="struct" required="Yes">
<cfargument name="filterChain" hint="the filter chain" type="conduit.core.filter.FilterChain" required="Yes">
<cfscript>
>//we know there will be a 'result', in the after filter
if(isSimplevalue(arguments.filterData.result))
{
arguments.filterData.result = Reverse(arguments.filterData.result);
}
>//push on to the next filter
arguments.filterChain.next();
</cfscript>
</cffunction>
</cfcomponent>
The doFilter() method is the method that manipulates the incoming data,
in this case, looking at the 'result' argument that is being passed in,
and if it's a simple value, reversing it.
The Filter is then passed on to any subsequent filters, by calling the
next() method on the filterChain. If we didn't want filtering to
continue, we could simply skip that step.
We then configure this ReverseFilter against the CFSerialiser, to convert outgoing data that is going to Flex, like so:
<destination id="Conduit">
<channels>
<channel ref="my-cfamf" />
</channels>
<adapter ref="conduit" />
<properties>
<source>*</source>
<cfcs>
<!--- cfc config --->
</cfcs>
<filters>
<!-- This is an example filter that reverses strings -->
<filter>
<path>conduit.core.filter.example.ReverseFilter</path>
<apply-after>serialiser</apply-after>
</filter>
</filters>
</properties>
</destination>
The 'apply-after' section, tells Conduit to fire the ReverseFilter
after the processing of the CFSerialiser, rather than before, although
in this case, it won't make that much difference.
This is just a simple example for the sort of things that can be done
with Conduit Filters, but it should give you a good idea on how with a
few simple filters, you can drastically change the way data is sent to
and from Flex and ColdFusion.
If you want to read more about Conduit, please have a look at the
wiki, and daily builds can be downloaded from the
google group.
Several days ago, cf.Objective(ANZ) put out a call for speakers.
For those who have missed the announcements, cf.Objective(ANZ), is bringing the famous cf.Objective() conference to Melbourne Australia.
To steal some of the copy straight from the website:
Topics we're looking for fit into (but are not limited to) the following major categories:
Architecture and Design:
OOP, Design Patterns, Frameworks, Modeling, Refactoring Legacy Apps, Persistence etc.
RIA:
LC DS and CF, Ajax/Flex with CF, BlazeDS and CF etc.
Process and Methodology:
Agile Development, SOA, Managing large CF architectures, Debugging and Metrics etc.
Integration and Testing:
CF and Java, Build and Deployment processes, Server tuning, Unit Testing etc.
Please let us know by April 24 2009 if you're interested by emailing speakers@cfobjective.com.au
with a short blurb about yourself and the topics you'd be interested in presenting on.
cf.Objective(ANZ)
will provide speaker accommodation for the night of the 12th to the
13th of November 2009 at the conference venue (Renaissance Hotel in
Melbourne). At this stage we unfortunately can't provide any further
financial assistance with travel cost or other expenses.
So if you wish to speak, or if you wish to hear about a particular topic, please feel free to send an email to the above address to let people know.
If you want to know more, you can check out the website , sign up to the website, and also follow the twitter feed !
Hope to see you all there!
As per usual, I'm late with my announcement that I will speaking at
cf.Objective() again this year. It is actually really interesting to
see the schedule contain so many sessions that cover OO concepts, and specifically, how to use common tools and development practices to make OO development in a web application context so much easier, better and faster.
This is obviously a real trend in the ColdFusion thoughtscape, with sessions like:
- Introduction to OO Modeling and Design - Brian Kotek
- Taking Code Reuse to a Higher Level - Jeff Chastian
- Building an Object Oriented Model - Bob Silverberg
- RAD OO in Code - Peter Bell
- Atomic Reactor - Mark Drew
- Leveraging Basic Object Oriented Concepts and Design Patterns in ColdFusion - Phill Nacelli
- What to do When OO Fails you in ColdFusion - Brian Meloche
- The Best of Both Worlds: Java Backends with CFML Frontends - Matt Woodward
- Object Relational Mapping with ColdFusion - Jason Delmore
- An Object Oriented Approach to Validations - Bob Silverberg
- Leveraging Enterprise Open-Source Java in ColdFusion - Joe Rinehart
..and of course, my very own
Rapid OO Development with ColdFusion Frameworks.
If you are looking to do, or already are doing Object Oriented development in ColdFusion, this is quite obviously
the place to be!
In my Rapid OO talk, we are going to take a completely manually written OO
model, including persistence to the database, and slowly strip parts of
the code away, replacing them with a combination of ColdFusion
Frameworks and custom generic code, to give you some new tools in your 'OO Development tool-belt', that you can then go away and play with, and hopefully include in your day to day development practices.
Look forward to seeing you all there!
I'm a little late in posting this, but I had figured I should at least
mention that I am speaking at
WebDU this year again.
For those who haven't been before, WebDU
is a fantastic Adobe/Web technology focused conference that has been
happened in Sydney for quite a while now. This year I'm particularly
excited by a lot of the Flex/RIA content that is being presented.
I'll be there this year talking about a
Conduit , the ColdFusion adapter for Blaze DS,
which allows you to all sorts of weird and wacky things with data
transfer to and from Flex and ColdFusion. I'll be showing off some
cute and clever data transformations and interesting techniques for
data exposure using Conduit, so it should be a interesting presentation
for anyone doing ColdFusion and Flex development.
Look forward to seeing you all at WebDU!
Last year I got the opportunity to have dinner with the steering committee of cf.Objective()
in the United States, and the topic of bringing cf.Objective() over the
Australia was brought up. I was instantly excited by the idea as I had
always had such a wonderful time at cf.Objective() in the US, and
had found it such a marvelous ColdFusion learning experience that I've
been back every year since, and am speaking again this year.
Apparently, I've now been given the title of 'cf.Objective() Vice Instigator - Pacific Operations ', for generally just being that annoying guy in the background going 'so what do we do next?', and harassing people on IM to make sure they show up for teleconferences.
Having
a stack of focused, ColdFusion specific, Enterprise software
development knowledge coming directly into Melbourne is going to be
great for local ColdFusion ecosystem, both for Melbourne, and also for
the Australia and New Zealand region.
Speaking from
experience, simply having a lot of smart minds that do enterprise
ColdFusion development, in the same place, at the same time, talking
ColdFusion, means that all sorts of interesting ideas and opportunities
get discussed, developed, and often worked on in the halls of the
conference.
The conference dates are the 12th and 13th of November, 2009.
We are still in the process of lining up sponsors and speakers, so if you are interested in either, please check out the website for more details and/or sign up for the mailing list, so you can be appraised of further developments.
If you are interested in coming to cf.Objective(ANZ), please sign up for the mailing list as well, so you can stay abreast of the latest and greatest news for the conference.
Oh, and the hotel is gorgeous, you should check out the online video!
I look forward to seeing you all there!
Not a huge amount to report here, this release fixes a bug in which if there is no 'init' method on a CFC, ColdDoc ends up throwing an error.
You can download ColdDoc 0.2 from here .
We are conducting a survey to try and tailor CFUG Melbourne to best fit
the ColdFusion users in Melbourne, and provide you guys with the best
CFUG we can.
Please take 10 minutes to fill out this quick survey on Google Docs, we would greatly appreciate it:
http://spreadsheets.google.com/viewform?key=pJCuG-fQNocTuTRAdDmagGg&hl=en
There are also prizes for filling out the survey! If you write your
email at the end of the survey, you will be in the running for:
1st prize: An Adobe Backpack
2nd Prize: A copy of the latest Fusion Authority Quarterly Update (Vol III, Issue 1)
3rd Prize: A Metal Adobe Lunchbox
More details on the user group can be found on the Adobe Groups page .
Thanks for taking the time, and look forward to seeing you all at a CFUG meeting this year.
Since I've been working with
Conduit,
there have been several 'enhancements' to the Flex Remoting that I've
implemented, above and beyond what ColdFusion provides out of the box.
These are mostly little things, but since Conduit is Open Source, we
are able to incorporate them into how Conduit works, and give us some
new tools to use when we set up Remoting.
One of the first enhancements I made, was around the fact that every time Flex invokes a CFC through Remoting,
it gets created on every request. Quite often, this is simply not
necessary, as the CFC in question has no state, or maybe you
want the CFC to maintain state, but you have to jump through some hoops to get that to happen.
In Conduit, you can now set a:
<cfcomponent remoteScope="application" >...</cfcomponent>
...and the CFC will be stored inside the specified shared scope.
For example, here is a CFC that gets stored in the session scope
when invoked, and keeps a simple incrementing number each time its
remote method gets called:
<cfcomponent hint="proxy stored in a share scope" output="false" remoteScope="session">
<cfscript>
instance.value = 1;
</cfscript>
<cffunction name="getIncrementingValue" hint="returns a value, and increments it by 1" access="remote" returntype="numeric" output="false">
<cfreturn instance.value++ />
</cffunction>
</cfcomponent>
So you can see here how we are able to maintain state between calls, within a single CFC.
The other enhancements I made, revolve around passing null values back and forth from AS3 Objects, and ColdFusion.
As we all know, there is no
null as such in ColdFusion, which can make dealing with null values from Flex slightly problematic.
I've added two new enhancements to <cfproperty> for Value Objects to help with this issue -
nullvalue and
nullmethod.
nullvalue provides a value for a given property to be used when
a null value is found in a Flex AS3 object coming down to ColdFusion.
This is particularly applicable when using getter/setters for your
properties on your ColdFusion Value Objects.
For example:
<cfcomponent output="false" alias="tests.cfml.cfc.model.Basic">
<cfproperty name="date" type="date" nullvalue="1-1-1900">
<cffunction name="getDate" access="public" returntype="date" output="false">
<cfreturn instance.date />
</cffunction>
<cffunction name="setDate" access="public" returntype="void" output="false">
<cfargument name="date" type="date" required="true">
<cfset instance.date = arguments.date />
</cffunction>
</cfcomponent>
...will set 'Date' to the 1-1-1900 if the AS3 Object's 'date' property
is null. Also, if sending data back up to Flex from CF, the 'date'
property is set to null on the Flex side, if the date value is
1-1-1900. This gives you a type safe way of dealing with null values
coming down from Flex.
The
nullMethod option for dealing with null values becomes more useful when dealing with object composition in Value Objects.
The
nullmethod attribute specifies a method on the CFC to be fired, when encountering a null value for the specified method, e.g.
<cfcomponent output="false" alias="tests.cfml.cfc.model.Basic">
<cfproperty name="simple" type="tests.cfml.cfc.model.Simple" nullmethod="removeSimple">
<cffunction name="setSimple" access="public" returntype="void" output="false">
<cfargument name="Simple" type="Simple" required="true">
<cfset instance.Simple = arguments.Simple />
</cffunction>
<cffunction name="removeSimple" hint="remove simple" access="public" returntype="void" output="false">
<cfset StructDelete(instance, "Simple") />
</cffunction>
</cfcomponent>
So in this case, whenever the property 'Simple' comes back from Flex as null, the method 'removeSimple' is called instead of attempting to set the property.
Now you can start to see what we can do with Conduit, since we can control all aspects of the Remoting process. So the question is - if you could change any aspect of ColdFusion <=> Flex Remoting, what would it be? Let me know, and we can see if we can incorporate it into Conduit!
If you want to read more, check out
Differences between Conduit and ColdFusion remoting, in the Conduit documentation.
When throwing exceptions in ColdFusion, we have the mighty <cfthrow> tag, which allows us to throw exceptions, with given names, and then catch them elsewhere in our application.
For example:
<cftry>
<cfthrow type="myException" message="This is my custom exception" detail="This is my custom exception detail"/>
<cfcatch type="myException" />
<cfset writeOutput("Caught Exception") />
</cfcatch>
</cftry>
Would output 'Caught Exception' onto the screen, as the cfcatch 'type' matches the name of the Exception being thrown.
This is all very well and good for a 'once off' throwing of an
exception, but what happens when an Exception type is used across an
code base? Often I have found myself copy and pasting <cfthrow> statements, just so I can have consistent types, messages and details between Exceptions within a system.
Taking inspiration for the way in which Java has actual Exception
objects, which can be implemented and then reused, I came up with the
following solution, which I felt was quite elegant, and extensible.
It all starts with a single, lowly Exception.cfc, that looks a little like this:
<cfcomponent name="Exception" hint="Throws a given exception" output="false">
<cffunction name="init" hint="Constructor" access="public" returntype="void" output="false">
<cfargument name="message" hint="the message to throw" type="string" required="Yes">
<cfargument name="detail" hint="the detail in which to throw" type="string" required="Yes">
<cfthrow type="#getMetaData(this).name#" message="#arguments.message#" detail="#detail#">
</cffunction>
</cfcomponent>
..and while this doesn't look like much in and of itself, it becomes
quite useful when we start extending it, and using it create our own
Exception types, for example, we can rewrite the example above, as myException.cfc:
<cfcomponent name="myException" hint="Throws myException" extends="Exception" output="false">
<cffunction name="init" hint="Constructor" access="public" returntype="void" output="false">
<cfset super.init("This is my custom exception", "This is my custom exception detail") />
</cffunction>
</cfcomponent>
To then throw myException, I simply do:
<cftry>
<cfset createObject("component", "myException").init();
<cfcatch type="myException" />
<cfset writeOutput("Caught Exception") />
</cfcatch>
</cftry>
..and the exception will be thrown, with the type 'myException', which comes from the name of the CFC, without me having to write the same message, and detail over and over again.
To get more complicated, and to lift an example directly out of
Conduit , this is the exception that gets thrown when Conduit can't find a particular method on a CFC it's making a Flex Remoting call to.
<cfcomponent name="MethodNotFoundException" hint="Exception for method not being found" extends="Exception" output="false">
<cffunction name="init" hint="Constructor" access="public" returntype="void" output="false">
<cfargument name="component" hint="the component that the method is attempting to be called on" type="any" required="Yes">
<cfargument name="methodName" hint="the name of the method" type="string" required="Yes">
<cfscript>
super.init("The method '#arguments.methodName#' in '#getMetaData(arguments.component).name#' could not be found.",
"This method could not be found. Please ensure that it was spelled correctly, and is not private"
);
</cfscript>
</cffunction>
</cfcomponent>
In this case we take a component and a method name as an argument,
and
can build Exception messages and details, based on those arguments,
much like you can with Java Exceptions. However, the actual Exception
type in this example will actually end up being
'conduit.exceptions.MethodNotFoundException', as this CFC is found in
/conduit/exceptions/MethodNotFoundException.cfc.
Now that your Exceptions are encapsulated inside CFCs, you can use
them all over your code base, knowing the Exception type will not
change, and you don't have to copy and paste your messages and details
every time you want to use them. There are other ways of doing similar
things, but this is one way I found works well for me.
This project started with writing the
translation layer for doing Transfer to Flex communication, I ran into a
pretty nasty bug in ColdFusion's remoting implementation,
and then ended up in a place that solves all these problems, and is
incredibly flexible and powerful to boot! Quite the round trip, but
well worth it in the end.
Conduit requires that you set up
BlazeDS
on your machine, as well as install the conduit.jar file, and some CFCs
as well. I'm not going to go into too much details here, as you can
read about in the
Library Installation section of the documentation.
To configure Conduit, we open up our remoting-config.xml, and we add the Conduit adapter to the <adapters> section:
<adapter-definition id="conduit" class="com.compoundtheory.conduit.adapters.ColdFusionAdapter"/>
And now we continue by adding a new
Destination to our remoting-config.xml, which we will then call from
Flex via RemoteObject (examples of the configuration are provided in the downloads).
<destination id="Conduit">
<channels>
<channel ref="my-cfamf" />
</channels>
<adapter ref="conduit" />
<properties>
<source>*</source>
<cfcs>
<!--
Whether or not reload the CFCs below on each request.
Useful for debugging when building new invokers,serialisers
or deserialisers
-->
<reloadcfcs>false</reloadcfcs>
<!--
The CFC that invokes the remote method call
-->
<invoker>conduit.core.CFCInvoker</invoker>
<!--
Translates CF=>AS3
-->
<serialiser>conduit.core.CFSerialiser</serialiser>
<!--
Translates AS3=>CF
-->
<deserialiser>conduit.core.CFDeserialiser</deserialiser>
</cfcs>
</properties>
</destination>
The id of the destination can be whatever you like, but we started with
'Conduit', so we would know what we are calling from Flex.
The <adapter> is specified to use the conduit adapter, rather than the default cf-object adapter.
We then set a series of properties, most important of which are setup
within the <cfc> section. This section controls what CFCs are
called upon to perform various duties within the ColdFusion <=>
Flex communication process.
Just to emphasise this point - this means that the majority of the heavy lifting done by Conduit is done
with ColdFusion code.
This makes it really easy to extend, change, manipulate or debug. It
gives you almost complete control over the AS3<=>CF translation
process, without you having to know much about Java at all (I will
admit there are some Java classes involved).
We can see from there are three CFCs that the Conduit adapter has configured for it.
The <invoker>
This is the CFC that does the actual method calling. In the instance
of the conduit.core.CFCInvoker that comes with Conduit, all it does is
create an instance of the CFC that has been requested in the
<RemoteObject>'s
source attribute, and calls the passed with method name and any parameters that were passed down from Flex.
The code looks something like this (just to show you how simple it is):
<cffunction name="execute" hint="Creates a cfc, and invokes a methodon it" access="public" returntype="any" output="false">
<cfargument name="source" hint="the cfc source" type="string" required="Yes">
<cfargument name="methodName" hint="the name of the method" type="string" required="Yes">
<cfargument name="params" hint="the parameters to pass in" type="any" required="Yes">
<cfscript>
var local = {};
var object = createObject("component", "#arguments.source#");
</cfscript>
<cfinvoke component="#object#" method="#arguments.methodName#"argumentcollection="#arguments.params#"returnvariable="local.return">
<cfif StructKeyExists(local, "return")>
<cfreturn local.return />
</cfif>
</cffunction>
Pretty straight forward, no?
The <serialiser>
The job of this CFC is to take whatever is returned from the
<invoker> and translate it into whatever you want to return back
to Flex. Since BlazeDS handles the AMF conversion part of it for us,
the conduit.core.CFSerialiser only really needs to convert CFCs in
Actionscript objects, set all their properties correct, and we are good to go.
The <deserialiser>
The deserialiser CFC's job it to take the incoming parameters that come
down from a Flex RemoteObject call, and translate them into something
usable for the <serialiser> CFC.
Again, since BlazeDS does a lot of the heavy lifting, the
conduit.core.CFDeseriailser's main job, is to convert Actionscript
Objects into their appropriate CFCs, and set all their properties
correctly.
Changing how things work
While there are plans to put some interesting enhancements for Conduit
to provide above and beyond what ColdFusion remoting already does, the
real power of Conduit comes from being able to write your own custom
Invoker, Serialiser or Deserialiser.
Say for example, when we send information from ColdFusion to Flex, we
want to reverse every Simple value (String, date, numeric) that we come
across. Don't ask me
why you would want to do this ;o), maybe you just like messing with your co-workers.
First thing we need to do is write our own custom CFSerialiser.cfc. For this example, I'm going to reuse the
conduit folder I would have setup, which already has a /conduit ColdFusion mapping pointing to it, and create a new folder called
reverse inside it.
So I create a new component, under /conduit/reverse, and call in
CFReverseSerialiser.cfc, and make it extend conduit.core.CFSerialiser.
We will now overwrite the
translate method, which handles what data gets converted, and how, depending on its data type.
The code would look something like this:
<cfcomponent output="false" extends="conduit.core.CFSeriaiser" hint="Component for serialising CF=>AS3, in reverse">
<cffunction name="translate" hint="translation function for objects,reverses simple values" access="private" returntype="any"output="false">
<cfargument name="object" hint="the object to serialise" type="any" required="no">
<cfargument name="cache" hint="local reference cache for cyclic graphs" type="any" required="Yes">
<cfscript>
if(isSimpleValue(arguments.object)
{
>//if it's simple, then reverse it!
return reverse(arguments.object);
}
else
{
return super.translate(argumentCollection=arguments);
}
</cfscript>
</cffunction>
</cfcomponent>
And we can configure a special 'ConduitReverse' destination for anyone who wants reversed Strings in their code:
<destination id="ConduitReverse">
<channels>
<channel ref="my-cfamf" />
</channels>
<adapter ref="conduit" />
<properties>
<source>*</source>
<cfcs>
<reloadcfcs>false</reloadcfcs>
<invoker>conduit.core.CFCInvoker</invoker>
<serialiser>conduit.reverse.CFReverseSerialiser</serialiser>
<deserialiser>conduit.core.CFDeserialiser</deserialiser>
</cfcs>
</properties>
</destination>
As you can see, we can do almost anything we want to the communication
process, simply by extending the core components (or even writing brand
new ones) and because its at a low level, its almost completely
seamless to those who are writing the ColdFusion and/or Flex code. That
being said, its open source, so we can add extra logging and/or
debugging as we need. No more failing silently!
Trying it out
This is still Alpha code, and there is a
lot of logging
currently in it, but there is enough there for you to start playing.
Code is in SVN, and daily builds are available from the
Google Group (saves you having to compile the .jar file). The
documentation is slowly getting fleshed out, but there is enough there to get started.
I am more than happy to get
code contributions, and/or ideas for how the to expand on the current ColdFusion remoting feature so send through whatever you have.
I hope you enjoy
Conduit!
When writing the Flex <=> Transfer integration, coming across a cyclic object graph is pretty common, as onetomany
compositions generate a bi-directional relationship between 2 objects.
I.e. A Parent knows about its children, and children know about their
parents.
It is also quite possible for situations to occur, where Object A knows
about B, B knows about C, and C knows about A, and therefore having a
complete circle.
What confused me no end pretty much all day yesterday, is that when
trying to send such an Object data structure up to Flex, theAMF Serialiser, doesn't send it up correctly, instead, it decides to replace one Object value with 'null', in an attempt to break the circle.
To give you an example, I set up a CFC, which had a UUID for identity, and a reference to a child (download the code below to have a look), and an appropriate VO on the Flex side.
I then sent this to Flex, as a cyclic graph of objects, like so:
<cfcomponent output="false">
<cffunction name="getRecursive" hint="returns a circular CFC structure" access="remote" returntype="flexrecursive.Recursive" output="false">
<cfscript>
var a = createObject("component", "flexrecursive.Recursive");
var b = createObject("component", "flexrecursive.Recursive");
var c = createObject("component", "flexrecursive.Recursive");
a.setChild(b);
b.setChild(c);
c.setChild(a);
return a;
</cfscript>
</cffunction>
</cfcomponent>
So, like above, A knows about B, B knows about C, and C knows about A.
I then passed this up to Flex, and logged whether or not the children in the collection of Objects was null.
In theory, I should be able to run the following code quite happily, without fear of a NullPointerException:
//rec is the returned objects to Flex
trace(rec.id);
trace(rec.child.id);
trace(rec.child.child.id);
//this should output the same id as the 1st line, as they are the the same object
trace(rec.child.child.child.id);
But what happens when I run my test code is:
* retrieved recursive CFCs, should be 3 items in a recursive graph:
rec.id:
id: F47E1371-C2A7-D498-1F6497AA3F3AEBC8
rec.child.id:
id: F47E1375-A404-D99C-50675EB12C98F9B9
rec.child.child.id:
id: F47E1378-A4AA-57E4-192C2A59D73A9832
rec.child.child.child.id:
This child is null!!!
The Serialiser simply replaces 'null' for the last place in the graph, rather than having a reference to the first Object!
I've run this code through Charles, and have confirmed this is also the structure CF sends in AMF as well.
Looking at the AMF Specification, I read:
'Objects can be sent as a reference to a previously occurring Object by using an index to
the implicit object reference table. Further more, trait information can also be sent as a
reference to a previously occurring set of traits by using an index to the implicit traits
reference table.'
So it should be possibly to handle a cyclic graph of objects, as
you could simply insert a reference to a previously serialised
object... so what gives ColdFusion? How come this weird and wacky
behaviour?
This test was run with ColdFusion 8.01, and Flex 3.2.
If you wish to download my CF and Flex test bed, you can from here.
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
ColdDoc 0.1 is finally ready as a release version.
ColdDoc is a port of JavaDoc for ColdFusion, and generates static .html files that display API information for a collection of CFCs.
ColdDoc was initially written to output the API documentation for Transfer, which can be seen here .
The benefits of outputting documentation to static html files, rather than doing it dynamically include:
- Less processing at run time
- Documentation can be linked to, without fear of page names and/or anchors changing
- The ability to generate API details such as Direct known subclasses and Method Overrides information which would be too expensive to determine at run time.
Currently ColdDoc only supports a single root path, and is missing some JavaDoc implementations, such as a Index page, and Interface documentation, and can only run on ColdFusion 8.
ColdDoc can be downloaded from here .
The last Melbourne CFUG for the year!
Location:
NGA.net, Level 2, 17 Raglan St, South Melbourne
Map: http://link.toolbot.com/google.com/73016
When:
November 20, Meeting starts at 7:00, so get there before hand (doors
open at 6:30).
Agenda:
Steve Onnis talks about virtualisation
Steve will be presenting on server virtualisation using VMWare. VMWare allows you to create
virtual operating systems that run independent of your main operating systems giving you the
flexibility to create development environments for testing.
Demonstrating the setup process and vm tools, you will gain an understanding of what virtualisation
is and an insight into what virtualisation can do for you.
If you are going to attend, please RSVP to mark (dot) mandel (at) gmail.com.
Only those that RSVP are eligible for the door prizes, so make sure you apply!
See the CFUG Melbourne Calendar at:
http://www.cfcentral.com.au/Events/index.cfm
Or add to your Google Calendar - search for 'CFUG Melbourne'.
As per usual, we'll grab pizza during the evening, so we have
something to scoff down!
Look forward to seeing you all there.
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
As per usual, this is something that came out of my work with
Transfer, but is something that applies to any ColdFusion application that exists.
So in the context of ColdFusion, what exactly are we referring to when we say
Thread? Generally the first thing we think of is <cfthread>,
which executes some code on its own given Thread. But, we should also
remember that the original page that was executing, is its own Thread
as well. If we run a scheduled task, that is also it's own Thread.
Wikipedia defines a
Thread very well:
"A
thread in computer science is short for a
thread of execution. Threads are a way for a program to split itself into two or more simultaneously (or pseudo-simultaneously) running tasks..."
So when looking at Threads, we can consider:
- Any Coldfusion page execution,
- Any remote CFC execution
- Any Scheduled Task execution
- Any CFThread execution
To be its own Thread, because, it is!
So what defines a
Selfish Thread?
A Selfish Thread is a thread that takes up almost all of the CPU's processing, without allowing any other Thread to be able to utilise the CPU at all.
Some code like this, would be a good example of a Thread being selfish -
<cfscript>
for(counter = 1; counter <= 10000; counter++)
{
writeOutPut(counter & "<br/>");
}
</cfscript>
It's a very tight loop, and there is no waiting, or pausing, or 'room'
for any other processing to do anything else while this loop processes.
Now it should be noted, that a Selfish Thread may not necessarily be a
bad thing. In many instances, we want this loop to completed, without
waiting for any other Thread to interrupt it. But in cases when Thread
execution can go on for a long time, this can be highly disruptive to
an application, as nothing else can be done during that time.
The common CF solution I often see for this, is the scheduled task that
runs at 3:00am, so that it doesn't bother any of the users. This can
work perfectly well for many applications, but what if your application
is 24 hours? Or is something that has to run every hour, what do you do
then?
Before we get into this too much, I want to make note of something - managing Threads is bit
black magic, and a bit
trial and error. Since Threads are managed differently per OS, and there are differences per JVM, some of these techniques will work, and some will not, so make sure you
test everything thoroughly so you know that it is affective for your OS and JVM configuration.
The other thing to note, is that any Thread that is running, is an actual instance of th Java object
java.lang.Thread. If at any point and time we want access to the actual Thread object that the given process is running we can run:
currentThread = createObject("java", "java.lang.Thread").currentThread();
Will return a reference to the currently executing Thread object, which will be very handy as we move along.
The first thing we should look at, is <cfthread>. CFThread has a 'priority' attribute that can be set to 'HIGH', 'NORMAL' or 'LOW', which
should control
the level of priority that a Thread has. For example, a HIGH priority
thread should have processing precedence over a LOW priority thread.
For example:
<cfthread action="run" name="foo1" priority="LOW">
<!--- do some processing --->
</cfthread>
In reality, I've not seen this actually do much (in
my tests), and it does not seem to actually effect a Thread's
Thread.getPriority(),
which we will talk about later. That being said, there may be some
other mechanism under the hood, and its not going to hurt anything if
you choose to use it.
From here, we can look at setting a Threads priority, which can be
applied to any CF based Thread (i.e. pages, scheduled tasks, cfthread
etc). A Threads priority goes from 1 to 10, where 1 is the lowest
priority, and 10 is the highest. 5 is usually considered 'Normal'.
In theory, a lower priority Thread should give way to a high priority
Thread whenever the higher priority Thread requires CPU processing
time. As stated earlier, depending onJVM and OS, this may, or may not happen.
To set the Thread's priority, all you need to do is grab the current thread, like we did above and call:
currentThread.setPriority(2); //set it to a lower priority.
//do some processing...
Since ColdFusion tends to pool Thread (i.e. stores them for reuse), we
should reset the Thread's priority after we are done with it, so that
it doesn't stay that when it gets used to execute another piece of
code. e.g.
priority = currentThread.getPriority();
currentThread.setPriority(2); //set it to a lower priority.
//do some expensive, long running processing...
currentThread.setPriority(priority); //reset it
This way, when the Thread get re-used, the Priority is not set to
something that is inappropriate for the processing it is doing.
There are also mechanisms in Java that allow you tell the JVM when a good time is for the current thread to
yield to other threads that need to do some processing.
This simply
hints to the JVM that 'hey! now would be a really good time for me to pause for a second, if you wanted to do something else'. The JVM can totally ignore this if it chooses, and depending on OS and JVM, it may well do.
To do this, we call the static method
yield(), on java.lang.Thread, like so:
createObject("java", "java.lang.Thread").yield();
So we can now take our very selfish loop above, and do something similar to:
<cfscript>
for(counter = 1; counter <= 10000; counter++)
{
writeOutPut(counter & "<br/>");
createObject("java", "java.lang.Thread").yield(); //here is a good place to pause
}
</cfscript>
This is actually a very poor use of yield(), simply because in a
display, we would never want the server to pause when displaying some
data, but it displays how it works reasonably well.
The interesting thing is, yield() automatically resolves what the
current thread it is processing on, and works that way, rather than the
setPriority() method we saw above, which required us to use a specific Thread.
Quite probably the least useful, but the most consistent way of managing selfish threads, is by putting the thread to
sleep, which will allow other Threads access to the CPU while that thread is asleep.
This is the least useful, as
no matter what, the Thread
will pause. Nothing else may be happening on the server, but the
Thread will pause anyway, which can mean wasted cycles for whatever it
is you are doing.
That being said, this will always work, no matter what JVM or OS you are on, so there is a trade off.
There are three ways we can make the current thread sleep in ColdFusion,
In cfscript:
<cfscript>
sleep(1000);
</cfscript>
via a Tag
<cfthread action="sleep" duration="1000" />
And via Java,
currentThread = createObject("java", "java.lang.Thread").currentThread();
currentThread.sleep(1000);
Either way, in the above example, the current thread will pause for 1000 milliseconds.
In a real world example, there is no reason we can't combine these
techniques. If we had some processing we wanted to happen
asynchronously, but we knew it was going to take a while to complete,
we could do something like the following:
<cfthread action="run" name="foo1" priority="LOW">
currentThread = createObject("java", "java.lang.Thread").currentThread();
priority = currentThread.getPriority();
currentThread.setPriority(3); //set it to a lower priority.
for(counter = 1; counter <= 10000; counter++)
{
doSomethingExpensive();
createObject("java", "java.lang.Thread").yield(); //could pause here
}
currentThread.setPriority(priority); //reset it
</cfthread>
Which gives us multiple ways in which to tell Java to make sure that other Threads are able to access the CPU.
Next time you are looking at a long running, expensive process, you now have multiple options about how you want to manage it.
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
All,
This Month's CFUG is lined up!
Location:
NGA.net, Level 2, 17 Raglan St, South Melbourne
Map:
http://link.toolbot.com/google.com/73016
When:
16th of October, Meeting starts at 7:00, so get there before hand (doors
open at 6:30).
Agenda:
Recorded Presentation by Dan Wilson
Refactoring in ColdFusion
If you would like to know how to migrate an existing procedurally programmed application into an object oriented one, grab a chair and sit for a while. We'll discuss some sensible guidelines designed to help you make incremental changes towards OO nirvana. We'll also look at lots of code samples, we all like code samples, right?
If you are going to attend, please RSVP to mark (dot) mandel (at) gmail.com.
Only those that RSVP are eligible for the door prizes, so make sure you apply!
See the CFUG Melbourne Calendar at:
http://www.cfcentral.com.au/Events/index.cfm
Or add to your Google Calendar - search for 'CFUG Melbourne'.
As per usual, we'll grab pizza during the evening, so we have
something to scoff down!
Look forward to seeing you all there.
I came to the realisation the other day, that my blog has really
become almost totally announcement based. A series of presentation
announcements, eSeminars, ColdFusion User Groups, Code Releases and
various other 'hey! this is what is going on' posts.
It used to be chock full of weird and interesting things you could do
with ColdFusion, and combining with the Java layer that sits underneath
it to do even weirder and even more interesting things with that.
Sadly, I've been super busy over the past <insert long time
frame>, and I've let things slip.
That being said, I plan on returning to the original theme of this
blog, and start publishing some of the new things I've found out about
ColdFusion, and what can be done with it. I've got some interesting
ideas on article on ColdFusion and Threading, and also some more on
onMissingMethod and method injection and manipulation, so be prepared!
So what has kept me so busy? Quite a lot actually. In no particular order...
Consulting full time.
This
has been one of the biggest changes, and adjustments for me to make
over the past 6 months or so. That being said, it is going very well,
and I am keeping myself very busy (Almost
too busy!). I love working from home, and love the flexibility working for myself gives me.
I also find it is a really rewarding experience. Working for yourself
challenges you in a variety of ways, as you only have yourself to rely
on, and you are solely responsible to make it or break it.
While
mostly I've been doing Transfer related work, I've also been doing some
mentoring and memory leak analysis, all of which have been really
interesting. Coming from a background in which I have tended to work
on single projects for months/years at a time, having a variety of
undertakings going at once is a nice change.
Speaking at webDU
I didn't even blog about this, which was very remiss of me! But I went to
webDU, which was great fun. I gave a presentation on TQL, and although I had some projector issues, it seemed to go quite well.
Writing Transfer code
I also got very busy writing
Transfer
1.2 in my spare (?) time. I'm really getting excited about Transfer
all over again, and I think I have some really interesting ideas for
the future. The next version is almost mapped out in my head... but I
will wait until after the Transfer Survey before setting it in stone.
Writing DevNet Frameworks Article
Writing
articles always take far longer than you expect them to, but when they
finally get published, it feels really great. From the feedback I've
received the
Introduction to ColdFusion Frameworks has been well received, and given some people new to frameworks some perspective over the landscape.
Something Super Cool Special
Been spending some time doing something really, really cool, but I can't really talk about it yet...
Don't you hate it when people say that? ;o)
Technical Editing for FAQU
I've
also started technical editing for the Fusion Authority Quarterly
Update, which one was one of those random opportunities that show up
during a conversation I was having with Judith Dinowitz. Editing other
people's articles in an interesting endeavour, but (I find) much easier
than writing them in the first place ;o)
Went on Holiday
I actually hadn't had a proper holiday
in several years, and it has made a huge difference! Taking a solid
break for a few weeks has meant I've come back with a new passion for
writing code, and a variety of other things in my life. I always
forget how much change a holiday can bring.
So that's my life at the moment in a nut shell. Figured I would let people know what was going on.
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 !
Sorry about the late blog post on this! I just realised I hadn't put one up!
Location:
NGA.net, Level 2, 17 Raglan St, South Melbourne
Map: http://link.toolbot.com/google.com/73016
When:
21st of August, Meeting starts at 7:00, so get there before hand (doors open
at 6:30).
Agenda:
Dale Fraser
Dale Fraser is the Chief Technical Officer at CogState Limited. Working on
varied projects using ColdFusion, Flex and C++, CogState develop Cognitive
Testing Software use mostly in Clinical Trials.
Report Builder (The Hidden Gem)
People may or may not realise that a report builder is included with
ColdFusion since version 7. Along with this report design tool, comes some
additional ColdFusion tags to allow you to incorporate full featured
reporting into your application without additional expense. While Report
Builder is not as rich featured as Crystal Reports or similar, it provides
all the necessary components to create reports, and being part of ColdFusion
the code and syntax used within reports is ColdFusion code that will
familiar to you all.
So if you either don't know about the Report Builder or have never has a
chance to play with it, this introduction will get you started. Things
covered will include
1. Designing a Report (No Code)
2. General Reporting Concepts, grouping, totalling etc.
3. Passing a Query into a Report
4. Generating a Report silently to a PDF
5. Printing a Report silently to a local printer.
If you are going to attend, please RSVP to mark (dot) mandel (at) gmail
(dot) com.
Only those that RSVP are eligible for the door prizes, so make sure you
apply!
Or add to your Google Calendar - search for 'CFUG Melbourne'.
As per usual, we'll grab pizza during the evening, so we have something to
scoff down!
Look forward to seeing you all there.
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!
'How do I run ColdFusion 8 and 7 side by side on IIS?' or 'How can I run Railo and Adobe ColdFusion side by side, so I can share the same code base?', is a question I see quite regularly on CF lists.
I figured I would write up a blog post about how I go about solving this problem, since I do this a lot with my Transfer development.
The quick answer is - I don't. Trying to set up two different Application servers (CF7 vs CF8) on the same Web Server (IIS or Apache), is not something I desire to tackle, and seems way more convoluted than I care to trouble myself with.
The problem is usually broken down into:
- How do I test the same code on different versions of CF?
- How do I do so without having to move/sync files between machines?
- How do I set it up so it is easy to switch between versions of testing?
I solve each of these problems with Virtual Machines , and Shared Folders.
I have actually bought VMware's Workstation product, and I use it every day, but there are several free products out there that will do virtualisation for free.
First thing first, where does my actual CFML code sit? This sits on my primary machine, commonly referred to as the host machine, in VM speak. This is because the host has guest machines, namely of the Virtual Machine variety.
This means my CFML code is always available if I want to get my hands on it, and I can open it in Eclipse on my host machine as well, so I do all my editing and development with tools on my host machine.
I also do this because I can then share this code between virtual machines, using a mechanism called Shared folders.
So, now that I have my ColdFusion code, I create myself a new virtual machine. In my case, I use Ubuntu Server, because its lightweight, and I can get one up and running in about half an hour, but you could use XP, or anything that runs CF.
I then configure my virtual machine to share my host folder which contains my CFML, which is a setting I am able to configure within VMWare. This then becomes accessible at /mnt/hgfs/wwwroot in my Ubuntu server on my VM.
From here, I can now install ColdFusion 8 on this VM, and point it to /mnt/hgfs/wwwroot folder to serve cfml from.
I can then create extra virtual machines, one for each version of ColdFusion that I need.
When I need to test against a version of ColdFusion, all I need to do is start up the virtual machine in question, and browse to it. To switch versions of ColdFusion, I simply power the current one down, and start up another one.
I find this a much easier way to test the same code base between different versions of ColdFusion than trying to run them side by side.
We're back to regular CFUG's, after the break for WebDU, and I think we have a pretty interesting presentation lined up!
Location:
NGA.net, Level 2, 17 Raglan St, South Melbourne
Map: http://link.toolbot.com/google.com/73016
When:
17th of July, Meeting starts at 7:00, so get there before hand (doors open at 6:30).
Agenda:
Elliott Sprehn
Elliott is a software engineer at TeraTech, Inc. and the lead developer for the Conferences department. He has worked on a variety
of projects that include network services, realtime messaging systems, survey processors and most commonly web applications. As a strong advocate of web standards and accessibility he volunteers his free time to help new developers learn standards and best practices. He is also proponent of design patterns and draws his vision about application design from experience with many languages that include ColdFusion, Java, ruby and php.
Internals of the Adobe ColdFusion Server
To many CF developers the ColdFusion server is a block box that just works. This presentation takes a look at how the internals of the engine operate. The presentation covers how variables, scopes, functions, cfcs, custom tags, includes and java objects are implemented and how we can use this knowledge to do things not normally possible with the CF functions.
We'll look at how to implement several features in CF6, CF7 and CF8 like:
- Query Caching with the native CF query cache for queries that use
cfqueryparam.
- Create and execute queries that use cfqueryparam from cfscript.
- Create a function like CF8 to get database info for a datasource.
- Add global mappings by appending to a structure like this.mappings in CF8.
- Call functions and component methods and inspect the local scope
after their execution.
- Access the Application.cfc anywhere in an application.
- Allow passing arrays of values in the url scope instead of lists
when there are duplicate keys.
If you are going to attend, please RSVP to mark (dot) mandel (at) gmail (dot) com.
Only those that RSVP are eligible for the door prizes, so make sure you apply!
See the CFUG Melbourne Calendar at:
http://www.cfcentral.com.au/Events/index.cfm
Or add to your Google Calendar - search for 'CFUG Melbourne'.
As per usual, we'll grab pizza during the evening, so we have something to scoff down!
Look forward to seeing you all there.
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 .
Not a huge amount to report. This release simply re-orders some of the class loading, so it is child first.
This
is important in case you load up a newer (or older) version of a
library that ColdFusion already has access to, and you want to make
sure you get the library you want, not the library loaded by ColdFusion.
That and I fixed a bug in the JavaProxy.cfc that was sent to me a loooong time ago, that will fix an issue with resolving overloaded Java Methods.
JavaLoader can be downloaded from
here.
All,
Another CFUG on the way!
(Apologies for the delay in getting this out, I was in the US!)
Location:
NGA.net, Level 2, 17 Raglan St, South Melbourne
Map:
http://link.toolbot.com/google.com/73016
When:
15th of May, Meeting starts at 7:00, so get there before hand (doors open at 6:30).
Agenda:
Richard Herbert, CTO of
NGA.NET, Australia's leading e-recruitment company, has been working with ColdFusion since 1997 and version 2.0. With a background in the legal and training industries Richard has developed applications for retail, point of sale, aviation, training, manufacturing, government and of course the HR industry. Also Telstra (but who hasn't?)...
Since 2000 Richard has been building
NGA.NET's e-recruitment product and delivering solutions to Top 500 companies and federal government, where
NGA.NET have 100% market share. The application has gone through many evolutionary steps, with a brand new version built on mach-ii/ColdSpring/Transfer architecture in beta for 2008.
NGA.NET is in the BRW Fast 100 for 2008 - a list of Australia's top 100 fastest growing companies.
And the presentation...
...will be titled 'Usability for Developers - The Ugly Truth'. I will be seeking to provide the audience with an understanding of what the field of usability covers, how to apply usability methods during the development cycle and the surprising results you can achieve.
Depending on time, Mark Mandel will also take us through some of the CF9 discussion that occurred at the cf.Objective() conference, and give us some insight into what could possibly be coming in CF9.
If you are going to attend, please RSVP to mark -dot- mandel -at- gmail -dot- com.
Only those that RSVP are eligible for the door prizes, so make sure you apply!
See the CFUG Melbourne Calendar at:
http://www.cfcentral.com.au/Events/index.cfm
Or add to your Google Calendar - search for 'CFUG Melbourne'.
As per usual, we'll grab pizza during the evening, so we have something to scoff down!
Look forward to seeing you all there.
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...
Another CFUG Meeting for us all,
Location:
NGA.net, Level 2, 17 Raglan St, South Melbourne
Map: http://link.toolbot.com/google.com/73016
When:
17th of April, Meeting starts at 7:00, so get there before hand (doors open at 6:30).
Agenda:
Dale Fraser will be taking us through....
Flex for ColdFusion Developers
This talk will cover the basics of how to get setup on FlexBuilder to start writing Flex applications that will leverage your existing ColdFusion code.
Topics covered will several of the standard flex components and how to return various types of data from ColdFusion to flex. You will be amazed how easy it is.
Specifically:
Returning Simple Values
Returning Structures
Returning Arrays
Returning Queries
And how to bind these to common components in Flex such as
DataGrid
ComboBox
TextInput
We will also cover dymystifying the install and configuration of Flex to ColdFusion, and a simple way to setup the necessary mappings between the two. This is for anyone who loves ColdFusion and wants to start with Flex and for anyone who is trying to decide between Ajax & Flex.
Dale will also announce the launch of a new website on this night that specifically is aimed at the ColdFusion to Flex developers.
If you are going to attend, please RSVP to mark [dot] mandel [at] gmail [dot] com.
Only those that RSVP are eligible for the door prizes, so make sure you apply!
See the CFUG Melbourne Calendar at:
http://www.cfcentral.com.au/Events/index.cfm
Or add to your Google Calendar - search for 'CFUG Melbourne'.
As per usual, we'll grab pizza during the evening, so we have something to scoff down!
Look forward to seeing you all there.
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!
This was too clever to pass up (By the way, 'Neuro', or 'Neurotic' is me):
<DanWilson> Neuro doesn't have kids, he generates them
<jamiejackson> hope they don't inherit behavior
<DanWilson> behaviour is mixed in at runtime
Check out more silliness at #coldfusion on Dalnet irc!
The first CFUG of the new year!
Location:
NGA.net, Level 2, 17 Raglan St, South Melbourne
Map:
http://link.toolbot.com/google.com/73016
When:
21st February Meeting starts at 7:00, so get there before hand (doors open at 6:30).
Agenda:
Tonight we will be viewing and discussing Sean Corfield's Presentation on Design Patterns:
Come find out what design patterns are really about and how they can make your life easier. (You're probably already using some design
patterns, although you may not know it.) For this session, we have distilled decades of software engineering experience into a well-documented set of blueprints that can be applied to common problems to ensure clean, maintainable code.
Sean Corfield:
Sean is currently a freelance consultant. He has worked in IT for nearly twenty five years, starting out writing database systems and compilers then moving into mobile telecoms and then finally into web development about nine years ago. Along the way, he worked on the ISO and ANSI C++ Standards committees for eight years. Sean is a staunch advocate of software standards and best practice. He wrote C++ coding guidelines for several companies during the 90's and more recently maintaining the Macromedia ColdFusion MX Coding Guidelines and Mach II Development Guide, which are also published for the ColdFusion community. He has also given several seminar talks, both in- house and publicly, on these subjects. Sean has championed and contributed to a number of ColdFusion frameworks.
If you are going to attend, please RSVP to mark [dot] mandel [at] gmail [dot] com.
Only those that RSVP are eligible for the door prizes, so make sure you apply!
See the CFUG Melbourne Calendar at:
http://www.cfcentral.com.au/Events/index.cfm
Or add to your Google Calendar - search for 'CFUG Melbourne'.
As per usual, we'll grab pizza during the evening, so we have something to scoff down!
Look forward to seeing you all there.
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.
I was very excited to find out yesterday that I am now an Adobe Community Expert!
I'm really happy to be selected for the program, and hopefully I'll get
my little 'Adobe Community Expert' sticker up on this site soon, when I
have two minutes to breathe... ;o)
In case people missed this the CFAUSSIE List:
Flex 3 and AIR are getting close to launch and in preparation, the dobe Platform Evangelist team is travelling around the world, and to elbourne, to show off the great new features and some brand new
demos.
Flex 3 is a feature-packed release, adding new UI components like the advanced datagrid and improved CSS capabilities; powerful tooling additions like refactoring; and extensive testing tools including
memory and performance profiling, plus the addition of the automated testing framework to Flex Builder.
Adobe AIR is game-changing in so many ways, extending rich applications to the desktop, enabling access to the local file system, system tray, notifications and much more. Now you can write desktop applications using the same skills that you've been already using to create great web apps including both Flex and AJAX.
Don't miss out on the opportunity to see and hear about this highly anticipated release of Flex 3 and AIR during this special pre-release tour. Plus, in addition to giving away some one of a kind Flex/AIR branded goodies, we will also be raffling off a copy of Flex Builder 3 Professional (pending availability) and a full commercial copy of CS3 Web Premium at this event!
Melbourne event details...
Danny Dura is an Adobe Platform Evangelist at Adobe U.S.A, who will be in Melbourne for a special Flex/AIR evening meeting on Tuesday the 29th of January, 2008.
This will be a joint effort/presentation of the Flash UG and the ColdFusion User Group (CFUG), but the venue is still the same.
Many have expressed interest already, and this event will go ahead, but we would still like to get an idea about how many are coming for catering reasons, please express your interest via, if you have not done so already:
Edit ::: 18-Jan-2008 ::: This event is at (or even over) capacity. You can still apply for interest, but if you do not get an invite, you cannot attend. We just have too many people wanting to come! :) (which is not a bad thing)
http://www.techevents.com.au/flex/
This event will be catered for and we will be giving away a copy of Adobe Flex 3 (pending availability), as well as Creative Suite 3 Web Premium, all courtesy of Adobe. Please note you must have expressed interest once via the link above to been in for a chance to win the Adobe software.
Meeting time...
7PM, Pizza and refreshments at 6:30PM, doors open 6PM.
Tuesday the 29th of January, 2008.
Meeting location...
Edit ::: 24th of January ::: Due to unforeseen circumstances, there has been a last minute venue change. Please check your invite emails for the new address.
About Danny Dura...
Danny Dura is an Adobe Platform Evangelist - His main focus is AIR, Flash Player, ActionScript and Flex.
http://www.danieldura.com/
In case people missed it via the CFUG list, please put down interest ASAP, so we get everything confirmed.
We are happy to have some potentially very exciting news for our Cold
Fusion User Group for very early in the new year.
Danny Dura is an Adobe Platform Evangelist at Adobe U.S.A will be in
Melbourne in late January so we are making preliminary enquiries to
see if our members would be interested in attending a special
Flex/AIR night meeting.
The planned date would be the evening of Tuesday the 29th of January,
2008. If you don't know much about Flex or AIR, this will be right up
your alley. If you already know about Flex or AIR, this is a great
opportunity to be brought up to speed with Flex 3 and AIR pending
their release this year.
We realise that many of you will be away, given it's still the
holiday season. However, if you'd be interested in coming, please let
us know by simply clicking on the link below.
http://www.techevents.com.au/flex/
If the event goes ahead, it will be catered for and we will be giving
away a copy of Adobe Flex 3 (delivered after release), as well as
Creative Suite 3 Web Premium, all courtesy of Adobe.
About Danny Dura...
Danny Dura is an Adobe Platform Evangelist - His main focus is AIR,
Flash Player, ActionScript and Flex.
http://www.danieldura.com/
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.
A quick reminder for the last Melbourne CFUG Meeting of the YEAR!
Location:
NGA.net, Level 2, 17 Raglan St, South Melbourne
Map: http://link.toolbot.com/google.com/73016
When:
20TH of December, Meeting starts at 7:00, so get there before hand (doors
open at 6:30).
Agenda:
Dale Fraser will be presenting on CFAJAX!
ColdFusion 8 includes a lot of Ajax features, and if you think Ajax is
complicated, think again. Ajax will make some of the things you do now
easier, and if you can write ColdFusion code, you can easily use Ajax
with ColdFusion Ajax features. So if you have always wanted to do some
Ajax stuff or you want to learn what Ajax is, then this is for you.
Topics covered include:
- <CFAJAXPROXY to bind to controls and CFC's
- <CFGRID a DHTML grid with binding and paging
- <CFINPUT an autosuggest feature using Ajax binding
- The ColdFusion. javascript methods that come with ColdFusion 8
- And more
If you are going to attend, please RSVP to mark [dot] mandel [at] gmail [dot] com
Only those that RSVP are eligible for the door prizes, so make sure you apply!
See the CFUG Melbourne Calendar at:
http://www.cfcentral.com.au/Events/index.cfm
Or add to your Google Calendar - search for 'CFUG Melbourne'.
As per usual, we'll grab pizza during the evening, so we have
something to scoff down!
Look forward to seeing you all there.
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!
Melbourne CFCAMP was last week, and I have to say, I think it went really, really well.
We had an attendance of about 50 people, so it made it a good intimate
environment, and everyone asked lots of questions from the presenters,
which was great to see.
It was also a great chance to catch up for drinks with the likes of
Geoff Bowers,
Robin Hilliard, and
Peter Bell, who may be local to Australia, but rarely are in the same place at the same time. (Albeit Peter is new to Australia).
We all went out for a few drinks after the CFCAMP events had finished,
and the conversation continued over wine and beer, and we had a pretty
good discussion about all sorts of things... including Robin's
calculations about the Red Bull challenge (which I don't think were
ever fully explained)
I'd like to say a big thanks to Mark Blair, and all the Adobe staff
that came down to support the event, it was really appreciated, a good
time was had by all, and we even learnt a thing or two.
If anyone is still sitting on the fence on whether or not to go to Perth, I would highly recommend it.
Just reminding everyone that the Melbourne CFCAMP is on
this Thursday!!!
The agenda has been locked down, and can be seen at
http://cfcamp.pbwiki.com/Agenda:+Melbourne and I have to say, it looks really exciting!
We're going to be seeing coverage of topics ranging from ColdFusion 8
(Obviously), Flex integration, AIR Integration, Farcry, Transfer ORM,
LiveCycle Data Services and Code Generation, not to mention its going
to be a great opportunity to catch up with all the local developers,
and see what is going on in the local development community.
If you have yet to
register,
I would highly recommend that you do! It's going to be a really good
day! And considering its all free, what have you got to lose?
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!!!
Melbourne CFCAMP is looming on the 22nd of November and we're in the need for speakers!
Topics can include anything from case studies, to frameworks, to
development techniques, to pretty much anything that is related to
ColdFusion!
Speaking time is 30 minutes long (although if you need more time, it can be arranged).
So if you have something you can present, or even an
idea of something you can present, please write it up on the
WIKI page, and share it with the rest of us.
Remember, CFCAMP is for the CF community, so the more your help out, the more your help your community.
If you aren't going to speak, don't forget to
register for the event, it should be a great day, and the venerable Ben Forta will be down to join us well!
All,
A reminder for the upcoming Melbourne CFUG! Still in our new boardroom! No more
tiny cramped room for us!
Location:
NGA.net, Level 2, 17 Raglan St, South Melbourne
Map:
http://link.toolbot.com/google.com/73016
When:
18th of October, Meeting starts at 7:00, so get there before hand (doors
open at 6:30).
Agenda:
Luis Majano himself is going to talk to us over Connect about the
ColdBox Framework.
Luis Majano is a Computer Engineer currently employed at ESRI
(Environmental Systems Research Institute) in sunny Redlands,
California. Please Support GIS!!. He graduated from Florida
International University and holds an Advanced Coldfusion MX 6,7
Developer Certifications. He has over 7 years experience in software
development, networking and system design. He is also a freelance
engineer and president of Ortus Solutions, Corp.
Coldbox
ColdBox is a proven event-driven CFC based ColdFusion Framework,
specifically designed for high availability web applications. The
purpose behind ColdBox was to create a fast & stable development
methodology that could be shared among several developers. It makes
use of an MVC (Model View Controller) design pattern implemented via
CFC's. It uses event handler CFC's that hold all the code necessary to
prepare views, call model CFC's or a business layer (Soap/ws/xml/J2EE)
and render views (HTML).
If you are going to attend, please RSVP to: mark [dot] mandel [at] gmail [dot] com
Only those that RSVP are eligible for the door prizes, so make sure you apply!
See the CFUG Melbourne Calendar at:
http://www.cfcentral.com.au/Events/index.cfm
Or add to your Google Calendar - search for 'CFUG Melbourne'.
As per usual, we'll grab pizza during the evening, so we have
something to scoff down!
Look forward to seeing you all there.
I've been given the opportunity to have my VPS where I am currently
hosting, with the free choice of OS, and pretty much any set up I want.
My initial thought was to set it up on
Ubuntu server, simply because, since switching to Ubuntu as my primary OS, I am
far more familiar with it as a Linux platform than I ever could be on a RedHat based one.
Now, I know that Ubuntu is not a supported platform, however -
1) I've been running CF8 on Ubuntu for development, and had 0 issues, from installation to use.
2) It's my VPS, so I'm happy to have a bit of an experiment.
3) I really don't feel like learning CentOS (Yeah.. I know.. lazy).
What do you guys reckon? Anyone been running CF8 on a Debian based
distro, and faced any issues? Can anyone think of any issues that could
trip me up?
Here is the release of
JavaLoader, version 0.5.
It pretty much does exactly the same thing that the previous versions
did,
however, it now works in ColdFusion 8, when the setting '
Disable access
to internal ColdFusion Java components' is turned on, as it uses its
own
JavaProxy CFC, which may often be turned on on shared hosts.
That's really about it, nothing more interesting than that.
Download JavaLoader v0.5 from
here.
One restriction I ran into today when doing some ColdFusion8 specific
performance enhancements to
Transfer
with <cfthread> is that you can't make a child thread within an already
created <cfthread>, so something like:
<cfthread action="run" name="a">
<cfthread action="run" name="b">
</cfthread>
</cfthread>
Will end up with lots of errors in your logs saying things like:
Error","cfthread-1","09/24/07","12:03:42",,"A: Thread B cannot be created. Child
threads are not supported."
Which is probably not what you actually want, as you would most likely like for
you code to actually
execute.
I'm not a huge fan of this limitation, but I can kind of understand where the
engineers are coming from - they don't want us CF developers to shoot ourselves
in the foot with ridiculously spawning multi threaded applications.
So, in my current situation, I wasn't too fussed if I had a whole, brand new
thread, but where my code was being run, I was so far down the stack of method
calls, I have no way of knowing if the original call had come from a standard
ColdFusion thread, or if it had come from a <cfthread> create thread, and
both of which were highly possible. However, if the execution wasn't coming from a <cfthread> created
thread, I wanted to execute my code inside a <cfthread> statement, as I
didn't care what the results where, and it would be faster for the end
user just to have the code run asynchronously.
If that just made your head hurt, I basically have a series of method calls that
look like:
A()
- makes a <cfthread> call, and calls C()
B()
- Does some work...
- Calls C() from the current thread.
C()
- makes a <cfthread> call, and then does some work
This is an over simplification of the process (mine is far more levels deep),
but gives you an idea of the trouble I was facing - the method call could go
through A() or through B(), and at the level of C() I wasn't aware of whether or
not I had started on the base ColdFusion thread or not.
I could have done this passing a parameter all the way down the method chain to
tell it if it should fire asynchronously, but that seemed like a lot of extra
work, and would have put a level of complexity within the whole process, and any
other process that was related to this, that it didn't seem like the appropriate
course of action.
So I thought to myself - there has to be a way to find out if the current thread
is a regular, ColdFusion thread,
or if it is a <cfthread> invoked
thread, and funnily enough, skipping into some Java, there is!
The first thing we have to do, is get access to the
java.lang.Thread
object:
Thread = createObject("java", "java.lang.Thread");
Now, the Thread object, has a great method
'
currentThread()',
which returns the current thread that the executing code is on:
currentThread = Thread.currentThread();
What is good to know, is that Threads in Java can exist in
ThreadGroups,
which is a handy way of grouping Threads together for common operations, and
other clever things. The nice thing here, is that ThreadGroups have a
method called
'
getName()'
which returns the name of the ThreadGroup.
Strangely enough, <cfthread> created threads live inside the ThreadGroup
named 'cfthread'! So now we can compare this against the name of the current
Thread's ThreadGroup
if(currentThread.getThreadGroup().getName() eq "cfthread")
{
//do something...
}
We can also wrap this up nicely into a quick little UDF:
<cffunction name="amInCFThread" hint="returns 'true' if the current thread is a cfthread, returns false otherwise" access="public"returntype="boolean" output="false">
<cfscript>
var Thread = createObject("java","java.lang.Thread");
if(Thread.currentThread().getThreadGroup().getName() eq "cfthread")
{
return true;
}
return false;
</cfscript>
</cffunction>
This function returns 'true' if the currently executing code is inside a
<cfthread>, and 'false' if it is not.
So now, in my function C() I can have:
C()
if(amInCFThread())
{
// run code
}
else
{
// make <cfthread> and run code.
}
And continue on my merry way!
Pretty handy if I'm not sure if my <cfthread>'d code will end up being run by other ColdFusion created threads or not.
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!
I need to know some numbers for if we can run a
CFCAMP in Melbourne, how many people would be likely to attend.
If you would be able to attend, please add a comment to this blog post,
along with how many people you would be able to bring along with you.
I'm currently trying to see what option we have in terms of venue to
incite Adobe to bring CFCAMP down to Melbourne, but if there isn't any
interest, better we know now!
Ideally this would shortly thereafter the other state's CFCAMP, so
please keep that in mind (short lead time, I know, but what can you
do?).
I hope we get a large response! :oD
All! It's a special CF8 Launch party!!!!
Location:
NGA.net, Level 2, 17 Raglan St, South Melbourne
Map:
http://link.toolbot.com/google.com/73016
When:
8th of August, Meeting starts at 6:30, so get there before hand -
** Please note, we are starting a little bit early this time, due to the crossover between states.
Agenda:
This is the official ColdFusion 8 Launch Party!
We will have presentations from Ben Forta via Connect, crossovers between the launch parties at all the other CFUGs across Australia and New Zealand, and prizes from Adobe for those people that attend!
Everyone is welcome, and if you haven't attended a single CFUG meeting yet, now is the time to come down!
If you are going to attend, please RSVP to mark [dot] mandel [at] gmail [dot] com.
See the CFUG Melbourne Calendar at:
http://www.cfcentral.com.au/Events/index.cfm
Or add to your Google Calendar - search for 'CFUG Melbourne'.
As per usual, we'll grab pizza during the evening, so we have
something to scoff down!
Look forward to seeing you all there.
This is a FAQ that was first written back in 2004, and has been
updated ever since, but outlines what I believe to be the best way to
ask questions on technical forums.
I think it's important to post it up, every now and again, so that people remember that it exists for a reason ;o)
How to Ask Questions the Smart Way
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
real